diff options
30 files changed, 609 insertions, 118 deletions
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 9df739c8f3c2..37581e5c5dcf 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -403,6 +403,13 @@ flag { } 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: "compose_bouncer" namespace: "systemui" description: "Use the new compose bouncer in SystemUI" diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index eda7bb0e7f6d..e5750d278bfe 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1729,10 +1729,18 @@ <dimen name="wallet_button_vertical_padding">8dp</dimen> <!-- Ongoing activity chip --> + <!-- The activity chip side padding, used with the default phone icon. --> <dimen name="ongoing_activity_chip_side_padding">12dp</dimen> + <!-- The activity chip side padding, used with an icon that has embedded padding (e.g. if the icon comes from the notification's smallIcon field). If the icon has padding, the chip itself can have less padding. --> + <dimen name="ongoing_activity_chip_side_padding_for_embedded_padding_icon">6dp</dimen> + <!-- The icon size, used with the default phone icon. --> <dimen name="ongoing_activity_chip_icon_size">16dp</dimen> - <!-- The padding between the icon and the text. --> + <!-- The icon size, used with an icon that has embedded padding. (If the icon has embedded padding, we need to make the whole icon larger so the icon itself doesn't look small.) --> + <dimen name="ongoing_activity_chip_embedded_padding_icon_size">22dp</dimen> + <!-- The padding between the icon and the text. Only used if the default phone icon is used. --> <dimen name="ongoing_activity_chip_icon_text_padding">4dp</dimen> + <!-- The end padding for the timer text view. Only used if an embedded padding icon is used. --> + <dimen name="ongoing_activity_chip_text_end_padding_for_embedded_padding_icon">6dp</dimen> <dimen name="ongoing_activity_chip_corner_radius">28dp</dimen> <!-- Status bar user chip --> diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index 212dae279387..e4f900d3a31a 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -132,6 +132,7 @@ <!-- Status bar --> <item type="id" name="status_bar_dot" /> + <item type="id" name="ongoing_activity_chip_custom_icon" /> <!-- Default display cutout on the physical top of screen --> <item type="id" name="display_cutout" /> diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/binder/IconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/common/ui/binder/IconViewBinder.kt index 64dedea3e6d4..108e22bc392b 100644 --- a/packages/SystemUI/src/com/android/systemui/common/ui/binder/IconViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/common/ui/binder/IconViewBinder.kt @@ -16,7 +16,6 @@ package com.android.systemui.common.ui.binder -import android.view.View import android.widget.ImageView import com.android.systemui.common.shared.model.Icon @@ -31,13 +30,4 @@ object IconViewBinder { is Icon.Resource -> view.setImageResource(icon.res) } } - - fun bindNullable(icon: Icon?, view: ImageView) { - if (icon != null) { - view.visibility = View.VISIBLE - bind(icon, view) - } else { - view.visibility = View.GONE - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt index 6e4038d85e03..59de2032c4f1 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt @@ -23,7 +23,13 @@ import com.android.server.notification.Flags.crossAppPoliteNotifications 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.keyguard.KeyguardBottomAreaRefactor import com.android.systemui.keyguard.MigrateClocksToBlueprint @@ -38,9 +44,9 @@ import com.android.systemui.statusbar.notification.interruption.VisualInterrupti import com.android.systemui.statusbar.notification.shared.NotificationAvalancheSuppression import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype +import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor -import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun import com.android.systemui.statusbar.notification.shared.PriorityPeopleSection import javax.inject.Inject @@ -77,6 +83,10 @@ class FlagDependencies @Inject constructor(featureFlags: FeatureFlagsClassic, ha // QS Fragment using Compose dependencies QSComposeFragment.token dependsOn NewQsUI.token + + // Status bar chip dependencies + statusBarCallChipNotificationIconToken dependsOn statusBarUseReposForCallChipToken + statusBarCallChipNotificationIconToken dependsOn statusBarScreenSharingChipsToken } private inline val politeNotifications @@ -96,4 +106,17 @@ class FlagDependencies @Inject constructor(featureFlags: FeatureFlagsClassic, ha 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 59fd0ca4513e..18ea0b445481 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,6 +18,7 @@ package com.android.systemui.statusbar.chips.call.ui.viewmodel 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 @@ -59,12 +60,24 @@ constructor( when (state) { is OngoingCallModel.NoCall -> OngoingActivityChipModel.Hidden() is OngoingCallModel.InCall -> { + val icon = + if ( + Flags.statusBarCallChipNotificationIcon() && + state.notificationIconView != null + ) { + OngoingActivityChipModel.ChipIcon.StatusBarView( + state.notificationIconView + ) + } else { + OngoingActivityChipModel.ChipIcon.Basic(phoneIcon) + } + // This block mimics OngoingCallController#updateChip. if (state.startTimeMs <= 0L) { // If the start time is invalid, don't show a timer and show just an // icon. See b/192379214. OngoingActivityChipModel.Shown.IconOnly( - icon = phoneIcon, + icon = icon, colors = ColorsModel.Themed, getOnClickListener(state), ) @@ -73,7 +86,7 @@ constructor( state.startTimeMs - systemClock.currentTimeMillis() + systemClock.elapsedRealtime() OngoingActivityChipModel.Shown.Timer( - icon = phoneIcon, + icon = icon, colors = ColorsModel.Themed, startTimeMs = startTimeInElapsedRealtime, getOnClickListener(state), diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt index d9b0504308f8..cf4e7072a7d1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt @@ -190,12 +190,14 @@ constructor( ): OngoingActivityChipModel.Shown { return OngoingActivityChipModel.Shown.Timer( icon = - Icon.Resource( - CAST_TO_OTHER_DEVICE_ICON, - // This string is "Casting screen" - ContentDescription.Resource( - R.string.cast_screen_to_other_device_chip_accessibility_label, - ), + OngoingActivityChipModel.ChipIcon.Basic( + Icon.Resource( + CAST_TO_OTHER_DEVICE_ICON, + // This string is "Casting screen" + ContentDescription.Resource( + R.string.cast_screen_to_other_device_chip_accessibility_label, + ), + ) ), colors = ColorsModel.Red, // TODO(b/332662551): Maybe use a MediaProjection API to fetch this time. @@ -213,10 +215,12 @@ constructor( private fun createIconOnlyCastChip(deviceName: String?): OngoingActivityChipModel.Shown { return OngoingActivityChipModel.Shown.IconOnly( icon = - Icon.Resource( - CAST_TO_OTHER_DEVICE_ICON, - // This string is just "Casting" - ContentDescription.Resource(R.string.accessibility_casting), + OngoingActivityChipModel.ChipIcon.Basic( + Icon.Resource( + CAST_TO_OTHER_DEVICE_ICON, + // This string is just "Casting" + ContentDescription.Resource(R.string.accessibility_casting), + ) ), colors = ColorsModel.Red, createDialogLaunchOnClickListener( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt index fcf3de42eb32..6ba4fefd6f3c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt @@ -78,11 +78,13 @@ constructor( is ScreenRecordChipModel.Recording -> { OngoingActivityChipModel.Shown.Timer( icon = - Icon.Resource( - ICON, - ContentDescription.Resource( - R.string.screenrecord_ongoing_screen_only, - ), + OngoingActivityChipModel.ChipIcon.Basic( + Icon.Resource( + ICON, + ContentDescription.Resource( + R.string.screenrecord_ongoing_screen_only, + ), + ) ), colors = ColorsModel.Red, startTimeMs = systemClock.elapsedRealtime(), diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt index 85973fca4326..7897f93b6496 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt @@ -110,9 +110,11 @@ constructor( ): OngoingActivityChipModel.Shown { return OngoingActivityChipModel.Shown.Timer( icon = - Icon.Resource( - SHARE_TO_APP_ICON, - ContentDescription.Resource(R.string.share_to_app_chip_accessibility_label), + OngoingActivityChipModel.ChipIcon.Basic( + Icon.Resource( + SHARE_TO_APP_ICON, + ContentDescription.Resource(R.string.share_to_app_chip_accessibility_label), + ) ), colors = ColorsModel.Red, // TODO(b/332662551): Maybe use a MediaProjection API to fetch this time. 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 17cf60bf2dc5..26a2f9139608 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,9 @@ 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 /** Model representing the display of an ongoing activity as a chip in the status bar. */ sealed class OngoingActivityChipModel { @@ -38,7 +40,7 @@ sealed class OngoingActivityChipModel { /** This chip should be shown with the given information. */ abstract class Shown( /** The icon to show on the chip. If null, no icon will be shown. */ - open val icon: Icon?, + open val icon: ChipIcon?, /** What colors to use for the chip. */ open val colors: ColorsModel, /** @@ -50,7 +52,7 @@ sealed class OngoingActivityChipModel { /** This chip shows only an icon and nothing else. */ data class IconOnly( - override val icon: Icon, + override val icon: ChipIcon, override val colors: ColorsModel, override val onClickListener: View.OnClickListener?, ) : Shown(icon, colors, onClickListener) { @@ -59,7 +61,7 @@ sealed class OngoingActivityChipModel { /** The chip shows a timer, counting up from [startTimeMs]. */ data class Timer( - override val icon: Icon, + override val icon: ChipIcon, override val colors: ColorsModel, /** * The time this event started, used to show the timer. @@ -88,4 +90,23 @@ sealed class OngoingActivityChipModel { override val logName = "Shown.Countdown" } } + + /** Represents an icon to show on the chip. */ + sealed interface ChipIcon { + /** + * The icon is a custom icon, which is set on [impl]. The icon was likely created by an + * external app. + */ + data class StatusBarView(val impl: StatusBarIconView) : ChipIcon { + init { + check(Flags.statusBarCallChipNotificationIcon()) { + "OngoingActivityChipModel.ChipIcon.StatusBarView created even though " + + "Flags.statusBarCallChipNotificationIcon is not enabled" + } + } + } + + /** The icon is a basic resource or drawable icon that System UI created internally. */ + data class Basic(val impl: Icon) : ChipIcon + } } 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 5d2d56acd0e4..10084517ec19 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,6 +25,8 @@ import android.graphics.drawable.Icon 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 import com.android.systemui.statusbar.notification.collection.NotificationEntry @@ -128,8 +130,14 @@ private class ActiveNotificationsStoreBuilder( return result } - private fun NotificationEntry.toModel(): ActiveNotificationModel = - existingModels.createOrReuse( + private fun NotificationEntry.toModel(): ActiveNotificationModel { + val statusBarChipIcon = + if (Flags.statusBarCallChipNotificationIcon()) { + icons.statusBarChipIcon + } else { + null + } + return existingModels.createOrReuse( key = key, groupKey = sbn.groupKey, whenTime = sbn.notification.`when`, @@ -142,6 +150,7 @@ private class ActiveNotificationsStoreBuilder( aodIcon = icons.aodIcon?.sourceIcon, shelfIcon = icons.shelfIcon?.sourceIcon, statusBarIcon = icons.statusBarIcon?.sourceIcon, + statusBarChipIconView = statusBarChipIcon, uid = sbn.uid, packageName = sbn.packageName, contentIntent = sbn.notification.contentIntent, @@ -150,6 +159,7 @@ private class ActiveNotificationsStoreBuilder( bucket = bucket, callType = sbn.toCallType(), ) + } } private fun ActiveNotificationsStore.createOrReuse( @@ -165,6 +175,7 @@ private fun ActiveNotificationsStore.createOrReuse( aodIcon: Icon?, shelfIcon: Icon?, statusBarIcon: Icon?, + statusBarChipIconView: StatusBarIconView?, uid: Int, packageName: String, contentIntent: PendingIntent?, @@ -187,6 +198,7 @@ private fun ActiveNotificationsStore.createOrReuse( aodIcon = aodIcon, shelfIcon = shelfIcon, statusBarIcon = statusBarIcon, + statusBarChipIconView = statusBarChipIconView, uid = uid, instanceId = instanceId, isGroupSummary = isGroupSummary, @@ -209,6 +221,7 @@ private fun ActiveNotificationsStore.createOrReuse( aodIcon = aodIcon, shelfIcon = shelfIcon, statusBarIcon = statusBarIcon, + statusBarChipIconView = statusBarChipIconView, uid = uid, instanceId = instanceId, isGroupSummary = isGroupSummary, @@ -232,6 +245,7 @@ private fun ActiveNotificationModel.isCurrent( aodIcon: Icon?, shelfIcon: Icon?, statusBarIcon: Icon?, + statusBarChipIconView: StatusBarIconView?, uid: Int, packageName: String, contentIntent: PendingIntent?, @@ -253,6 +267,7 @@ private fun ActiveNotificationModel.isCurrent( aodIcon != this.aodIcon -> false shelfIcon != this.shelfIcon -> false statusBarIcon != this.statusBarIcon -> false + statusBarChipIconView != this.statusBarChipIconView -> false uid != this.uid -> false instanceId != this.instanceId -> false isGroupSummary != this.isGroupSummary -> false 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 331d3cc4c21b..dc6ab4126337 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 @@ -123,6 +123,13 @@ constructor( // Construct the status bar icon view. val sbIcon = iconBuilder.createIconView(entry) sbIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE + val sbChipIcon: StatusBarIconView? + if (Flags.statusBarCallChipNotificationIcon()) { + sbChipIcon = iconBuilder.createIconView(entry) + sbChipIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE + } else { + sbChipIcon = null + } // Construct the shelf icon view. val shelfIcon = iconBuilder.createIconView(entry) @@ -139,9 +146,19 @@ constructor( try { setIcon(entry, normalIconDescriptor, sbIcon) + if (Flags.statusBarCallChipNotificationIcon() && sbChipIcon != null) { + setIcon(entry, normalIconDescriptor, sbChipIcon) + } setIcon(entry, sensitiveIconDescriptor, shelfIcon) setIcon(entry, sensitiveIconDescriptor, aodIcon) - entry.icons = IconPack.buildPack(sbIcon, shelfIcon, aodIcon, entry.icons) + entry.icons = + IconPack.buildPack( + sbIcon, + sbChipIcon, + shelfIcon, + aodIcon, + entry.icons, + ) } catch (e: InflationException) { entry.icons = IconPack.buildEmptyPack(entry.icons) throw e @@ -182,6 +199,11 @@ constructor( setIcon(entry, normalIconDescriptor, it) } + entry.icons.statusBarChipIcon?.let { + it.setNotification(entry.sbn, notificationContentDescription) + setIcon(entry, normalIconDescriptor, it) + } + entry.icons.shelfIcon?.let { it.setNotification(entry.sbn, notificationContentDescription) setIcon(entry, sensitiveIconDescriptor, it) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java index d029ce722af9..611cebcf6427 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java @@ -29,6 +29,7 @@ public final class IconPack { private final boolean mAreIconsAvailable; @Nullable private final StatusBarIconView mStatusBarIcon; + @Nullable private final StatusBarIconView mStatusBarChipIcon; @Nullable private final StatusBarIconView mShelfIcon; @Nullable private final StatusBarIconView mAodIcon; @@ -43,7 +44,7 @@ public final class IconPack { * haven't been inflated yet or there was an error while inflating them). */ public static IconPack buildEmptyPack(@Nullable IconPack fromSource) { - return new IconPack(false, null, null, null, fromSource); + return new IconPack(false, null, null, null, null, fromSource); } /** @@ -51,20 +52,23 @@ public final class IconPack { */ public static IconPack buildPack( @NonNull StatusBarIconView statusBarIcon, + @Nullable StatusBarIconView statusBarChipIcon, @NonNull StatusBarIconView shelfIcon, @NonNull StatusBarIconView aodIcon, @Nullable IconPack source) { - return new IconPack(true, statusBarIcon, shelfIcon, aodIcon, source); + return new IconPack(true, statusBarIcon, statusBarChipIcon, shelfIcon, aodIcon, source); } private IconPack( boolean areIconsAvailable, @Nullable StatusBarIconView statusBarIcon, + @Nullable StatusBarIconView statusBarChipIcon, @Nullable StatusBarIconView shelfIcon, @Nullable StatusBarIconView aodIcon, @Nullable IconPack source) { mAreIconsAvailable = areIconsAvailable; mStatusBarIcon = statusBarIcon; + mStatusBarChipIcon = statusBarChipIcon; mShelfIcon = shelfIcon; mAodIcon = aodIcon; if (source != null) { @@ -79,6 +83,17 @@ public final class IconPack { } /** + * The version of the notification icon that appears inside a chip within the status bar. + * + * Separate from {@link #getStatusBarIcon()} so that we don't have to worry about detaching and + * re-attaching the same view when the chip appears and hides. + */ + @Nullable + public StatusBarIconView getStatusBarChipIcon() { + return mStatusBarChipIcon; + } + + /** * The version of the icon that appears in the "shelf" at the bottom of the notification shade. * In general, this icon also appears somewhere on the notification and is "sucked" into the * shelf as the scrolls beyond it. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt index 6960791f7bcb..cf19938aa533 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.shared import android.app.PendingIntent import android.graphics.drawable.Icon +import com.android.systemui.statusbar.StatusBarIconView import com.android.systemui.statusbar.notification.stack.PriorityBucket /** @@ -59,6 +60,8 @@ data class ActiveNotificationModel( val shelfIcon: Icon?, /** Icon to display in the status bar. */ val statusBarIcon: Icon?, + /** Icon to display in the status bar chip. */ + val statusBarChipIconView: StatusBarIconView?, /** The notifying app's [packageName]'s uid. */ val uid: Int, /** The notifying app's packageName. */ 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 7af066646629..4368239c31f0 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 @@ -38,6 +38,7 @@ import com.android.systemui.log.LogBuffer import com.android.systemui.log.core.LogLevel import com.android.systemui.plugins.ActivityStarter import com.android.systemui.res.R +import com.android.systemui.statusbar.StatusBarIconView import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer import com.android.systemui.statusbar.chips.ui.view.ChipChronometer import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore @@ -114,6 +115,9 @@ constructor( 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( @@ -223,8 +227,15 @@ constructor( callNotificationInfo // This shouldn't happen, but protect against it in case ?: return OngoingCallModel.NoCall + val icon = + if (Flags.statusBarCallChipNotificationIcon()) { + currentInfo.notificationIconView + } else { + null + } return OngoingCallModel.InCall( startTimeMs = currentInfo.callStartTime, + notificationIconView = icon, intent = currentInfo.intent, ) } else { @@ -260,6 +271,7 @@ constructor( CallNotificationInfo( notifModel.key, notifModel.whenTime, + notifModel.statusBarChipIconView, notifModel.contentIntent, notifModel.uid, isOngoing = true, @@ -407,6 +419,8 @@ constructor( private data class CallNotificationInfo( val key: String, val callStartTime: Long, + /** The icon set as the [android.app.Notification.getSmallIcon] field. */ + val notificationIconView: StatusBarIconView?, val intent: PendingIntent?, val uid: Int, /** True if the call is currently ongoing (as opposed to incoming, screening, etc.). */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt index 2c4848776b66..34bff80ea919 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.phone.ongoingcall.shared.model import android.app.PendingIntent +import com.android.systemui.statusbar.StatusBarIconView /** Represents the state of any ongoing calls. */ sealed interface OngoingCallModel { @@ -31,7 +32,13 @@ sealed interface OngoingCallModel { * [com.android.systemui.util.time.SystemClock.currentTimeMillis], **not** * [com.android.systemui.util.time.SystemClock.elapsedRealtime]. This value can be 0 if the * user has started an outgoing call that hasn't been answered yet - see b/192379214. + * @property notificationIconView the [android.app.Notification.getSmallIcon] that's set on the + * call notification. We may use this icon in the chip instead of the default phone icon. * @property intent the intent associated with the call notification. */ - data class InCall(val startTimeMs: Long, val intent: PendingIntent?) : OngoingCallModel + data class InCall( + val startTimeMs: Long, + val notificationIconView: StatusBarIconView?, + val intent: PendingIntent?, + ) : OngoingCallModel } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt index 16bd7f830c66..d46aaf45b1a3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt @@ -18,9 +18,12 @@ package com.android.systemui.statusbar.pipeline.shared.ui.binder import android.animation.Animator import android.animation.AnimatorListenerAdapter +import android.annotation.IdRes import android.content.res.ColorStateList import android.graphics.drawable.GradientDrawable import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout import android.widget.ImageView import android.widget.TextView import androidx.lifecycle.Lifecycle @@ -31,6 +34,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.res.R import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.statusbar.StatusBarIconView import com.android.systemui.statusbar.chips.ui.binder.ChipChronometerBinder import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer @@ -90,7 +94,7 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa if (Flags.statusBarScreenSharingChips()) { val chipView: View = view.requireViewById(R.id.ongoing_activity_chip) val chipContext = chipView.context - val chipIconView: ImageView = + val chipDefaultIconView: ImageView = chipView.requireViewById(R.id.ongoing_activity_chip_icon) val chipTimeView: ChipChronometer = chipView.requireViewById(R.id.ongoing_activity_chip_time) @@ -105,16 +109,25 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa when (chipModel) { is OngoingActivityChipModel.Shown -> { // Data - IconViewBinder.bindNullable(chipModel.icon, chipIconView) + setChipIcon(chipModel, chipBackgroundView, chipDefaultIconView) setChipMainContent(chipModel, chipTextView, chipTimeView) chipView.setOnClickListener(chipModel.onClickListener) + updateChipPadding( + chipModel, + chipBackgroundView, + chipTextView, + chipTimeView, + ) // Accessibility setChipAccessibility(chipModel, chipView, chipBackgroundView) // Colors val textColor = chipModel.colors.text(chipContext) - chipIconView.imageTintList = ColorStateList.valueOf(textColor) + chipDefaultIconView.imageTintList = + ColorStateList.valueOf(textColor) + chipBackgroundView.getCustomIconView()?.imageTintList = + ColorStateList.valueOf(textColor) chipTimeView.setTextColor(textColor) chipTextView.setTextColor(textColor) (chipBackgroundView.background as GradientDrawable).color = @@ -151,6 +164,69 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa } } + private fun setChipIcon( + chipModel: OngoingActivityChipModel.Shown, + backgroundView: ChipBackgroundContainer, + defaultIconView: ImageView, + ) { + // Always remove any previously set custom icon. If we have a new custom icon, we'll re-add + // it. + backgroundView.removeView(backgroundView.getCustomIconView()) + + when (val icon = chipModel.icon) { + null -> { + defaultIconView.visibility = View.GONE + } + is OngoingActivityChipModel.ChipIcon.Basic -> { + IconViewBinder.bind(icon.impl, defaultIconView) + defaultIconView.visibility = View.VISIBLE + } + is OngoingActivityChipModel.ChipIcon.StatusBarView -> { + // Hide the default icon since we'll show this custom icon instead. + defaultIconView.visibility = View.GONE + + // Add the new custom icon: + // 1. Set up the right visual params. + val iconView = icon.impl + with(iconView) { + id = CUSTOM_ICON_VIEW_ID + // TODO(b/354930838): Update the content description to not include "phone" and + // maybe include the app name. + contentDescription = + context.resources.getString(R.string.ongoing_phone_call_content_description) + } + + // 2. If we just reinflated the view, we may need to detach the icon view from the + // old chip before we reattach it to the new one. + // See also: NotificationIconContainerViewBinder#bindIcons. + val currentParent = iconView.parent as? ViewGroup + if (currentParent != null && currentParent != backgroundView) { + currentParent.removeView(iconView) + currentParent.removeTransientView(iconView) + } + + // 3: Add the icon as the starting view. + backgroundView.addView( + iconView, + /* index= */ 0, + generateCustomIconLayoutParams(iconView), + ) + } + } + } + + private fun View.getCustomIconView(): StatusBarIconView? { + return this.findViewById(CUSTOM_ICON_VIEW_ID) + } + + private fun generateCustomIconLayoutParams(iconView: ImageView): FrameLayout.LayoutParams { + val customIconSize = + iconView.context.resources.getDimensionPixelSize( + R.dimen.ongoing_activity_chip_embedded_padding_icon_size + ) + return FrameLayout.LayoutParams(customIconSize, customIconSize) + } + private fun setChipMainContent( chipModel: OngoingActivityChipModel.Shown, chipTextView: TextView, @@ -180,37 +256,93 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa chipTimeView.visibility = View.GONE } } - updateChipTextPadding(chipModel, chipTextView, chipTimeView) } - private fun updateChipTextPadding( + private fun updateChipPadding( chipModel: OngoingActivityChipModel.Shown, + backgroundView: View, chipTextView: TextView, chipTimeView: ChipChronometer, ) { - val requiresPadding = chipModel.icon != null - if (requiresPadding) { - chipTextView.addChipTextPaddingStart() - chipTimeView.addChipTextPaddingStart() + if (chipModel.icon != null) { + if (chipModel.icon is OngoingActivityChipModel.ChipIcon.StatusBarView) { + // If the icon is a custom [StatusBarIconView], then it should've come from + // `Notification.smallIcon`, which is required to embed its own paddings. We need to + // adjust the other paddings to make everything look good :) + backgroundView.setBackgroundPaddingForEmbeddedPaddingIcon() + chipTextView.setTextPaddingForEmbeddedPaddingIcon() + chipTimeView.setTextPaddingForEmbeddedPaddingIcon() + } else { + backgroundView.setBackgroundPaddingForNormalIcon() + chipTextView.setTextPaddingForNormalIcon() + chipTimeView.setTextPaddingForNormalIcon() + } } else { - chipTextView.removeChipTextPaddingStart() - chipTimeView.removeChipTextPaddingStart() + backgroundView.setBackgroundPaddingForNoIcon() + chipTextView.setTextPaddingForNoIcon() + chipTimeView.setTextPaddingForNoIcon() } } - private fun View.addChipTextPaddingStart() { + private fun View.setTextPaddingForEmbeddedPaddingIcon() { + val newPaddingEnd = + context.resources.getDimensionPixelSize( + R.dimen.ongoing_activity_chip_text_end_padding_for_embedded_padding_icon + ) + setPaddingRelative( + // The icon should embed enough padding between the icon and time view. + /* start= */ 0, + this.paddingTop, + newPaddingEnd, + this.paddingBottom, + ) + } + + private fun View.setTextPaddingForNormalIcon() { this.setPaddingRelative( this.context.resources.getDimensionPixelSize( R.dimen.ongoing_activity_chip_icon_text_padding ), paddingTop, - paddingEnd, + // The background view will contain the right end padding. + /* end= */ 0, + paddingBottom, + ) + } + + private fun View.setTextPaddingForNoIcon() { + // The background view will have even start & end paddings, so we don't want the text view + // to add any additional padding. + this.setPaddingRelative(/* start= */ 0, paddingTop, /* end= */ 0, paddingBottom) + } + + private fun View.setBackgroundPaddingForEmbeddedPaddingIcon() { + val sidePadding = + context.resources.getDimensionPixelSize( + R.dimen.ongoing_activity_chip_side_padding_for_embedded_padding_icon + ) + setPaddingRelative( + sidePadding, + paddingTop, + sidePadding, + paddingBottom, + ) + } + + private fun View.setBackgroundPaddingForNormalIcon() { + val sidePadding = + context.resources.getDimensionPixelSize(R.dimen.ongoing_activity_chip_side_padding) + setPaddingRelative( + sidePadding, + paddingTop, + sidePadding, paddingBottom, ) } - private fun View.removeChipTextPaddingStart() { - this.setPaddingRelative(/* start= */ 0, paddingTop, paddingEnd, paddingBottom) + private fun View.setBackgroundPaddingForNoIcon() { + // The padding for the normal icon is also appropriate for no icon. + setBackgroundPaddingForNormalIcon() } private fun setChipAccessibility( @@ -269,6 +401,10 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa ) .start() } + + companion object { + @IdRes private val CUSTOM_ICON_VIEW_ID = R.id.ongoing_activity_chip_custom_icon + } } /** Listener for various events that may affect the status bar's visibility. */ diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorTest.kt index cd8a7407970f..8f41caf54ec8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorTest.kt @@ -23,6 +23,7 @@ import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope 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.google.common.truth.Truth.assertThat import kotlin.test.Test import kotlinx.coroutines.test.runTest @@ -39,7 +40,7 @@ class CallChipInteractorTest : SysuiTestCase() { kosmos.testScope.runTest { val latest by collectLastValue(underTest.ongoingCallState) - val inCall = OngoingCallModel.InCall(startTimeMs = 1000, intent = null) + val inCall = inCallModel(startTimeMs = 1000) repo.setOngoingCallState(inCall) assertThat(latest).isEqualTo(inCall) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt index 1a6b420dace5..ce79fbde77a3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt @@ -17,8 +17,11 @@ package com.android.systemui.statusbar.chips.call.ui.viewmodel import android.app.PendingIntent +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags import android.view.View 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 @@ -26,11 +29,13 @@ import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope import com.android.systemui.plugins.activityStarter import com.android.systemui.res.R +import com.android.systemui.statusbar.StatusBarIconView import com.android.systemui.statusbar.chips.ui.model.ColorsModel import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer 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.util.time.fakeSystemClock import com.google.common.truth.Truth.assertThat import kotlin.test.Test @@ -73,7 +78,7 @@ class CallChipViewModelTest : SysuiTestCase() { testScope.runTest { val latest by collectLastValue(underTest.chip) - repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 0, intent = null)) + repo.setOngoingCallState(inCallModel(startTimeMs = 0)) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java) } @@ -83,7 +88,7 @@ class CallChipViewModelTest : SysuiTestCase() { testScope.runTest { val latest by collectLastValue(underTest.chip) - repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = -2, intent = null)) + repo.setOngoingCallState(inCallModel(startTimeMs = -2)) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java) } @@ -93,7 +98,7 @@ class CallChipViewModelTest : SysuiTestCase() { testScope.runTest { val latest by collectLastValue(underTest.chip) - repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 345, intent = null)) + repo.setOngoingCallState(inCallModel(startTimeMs = 345)) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) } @@ -106,7 +111,7 @@ class CallChipViewModelTest : SysuiTestCase() { kosmos.fakeSystemClock.setCurrentTimeMillis(3000) kosmos.fakeSystemClock.setElapsedRealtime(400_000) - repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 1000, intent = null)) + repo.setOngoingCallState(inCallModel(startTimeMs = 1000)) // The OngoingCallModel start time is relative to currentTimeMillis, so this call // started 2000ms ago (1000 - 3000). The OngoingActivityChipModel start time needs to be @@ -117,29 +122,97 @@ class CallChipViewModelTest : SysuiTestCase() { } @Test - fun chip_positiveStartTime_iconIsPhone() = + @DisableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON) + fun chip_positiveStartTime_notifIconFlagOff_iconIsPhone() = testScope.runTest { val latest by collectLastValue(underTest.chip) - repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 1000, intent = null)) + repo.setOngoingCallState( + inCallModel(startTimeMs = 1000, notificationIcon = mock<StatusBarIconView>()) + ) + + assertThat((latest as OngoingActivityChipModel.Shown).icon) + .isInstanceOf(OngoingActivityChipModel.ChipIcon.Basic::class.java) + val icon = + (((latest as OngoingActivityChipModel.Shown).icon) + as OngoingActivityChipModel.ChipIcon.Basic) + .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) + fun chip_positiveStartTime_notifIconFlagOn_iconIsNotifIcon() = + testScope.runTest { + val latest by collectLastValue(underTest.chip) + + val notifIcon = mock<StatusBarIconView>() + 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 + @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 = mock<StatusBarIconView>()) + ) + + assertThat((latest as OngoingActivityChipModel.Shown).icon) + .isInstanceOf(OngoingActivityChipModel.ChipIcon.Basic::class.java) + val icon = + (((latest as OngoingActivityChipModel.Shown).icon) + as OngoingActivityChipModel.ChipIcon.Basic) + .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) + fun chip_zeroStartTime_notifIconFlagOn_iconIsNotifIcon() = + testScope.runTest { + val latest by collectLastValue(underTest.chip) + + val notifIcon = mock<StatusBarIconView>() + repo.setOngoingCallState(inCallModel(startTimeMs = 0, notificationIcon = notifIcon)) - assertThat(((latest as OngoingActivityChipModel.Shown).icon as Icon.Resource).res) - .isEqualTo(com.android.internal.R.drawable.ic_phone) - assertThat((latest as OngoingActivityChipModel.Shown).icon!!.contentDescription) - .isNotNull() + 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 - fun chip_zeroStartTime_iconIsPhone() = + @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON) + fun chip_notifIconFlagOn_butNullNotifIcon_iconIsPhone() = testScope.runTest { val latest by collectLastValue(underTest.chip) - repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 0, intent = null)) + repo.setOngoingCallState(inCallModel(startTimeMs = 1000, notificationIcon = null)) - assertThat(((latest as OngoingActivityChipModel.Shown).icon as Icon.Resource).res) - .isEqualTo(com.android.internal.R.drawable.ic_phone) - assertThat((latest as OngoingActivityChipModel.Shown).icon!!.contentDescription) - .isNotNull() + assertThat((latest as OngoingActivityChipModel.Shown).icon) + .isInstanceOf(OngoingActivityChipModel.ChipIcon.Basic::class.java) + val icon = + (((latest as OngoingActivityChipModel.Shown).icon) + as OngoingActivityChipModel.ChipIcon.Basic) + .impl as Icon.Resource + assertThat(icon.res).isEqualTo(com.android.internal.R.drawable.ic_phone) + assertThat(icon.contentDescription).isNotNull() } @Test @@ -147,7 +220,7 @@ class CallChipViewModelTest : SysuiTestCase() { testScope.runTest { val latest by collectLastValue(underTest.chip) - repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 1000, intent = null)) + repo.setOngoingCallState(inCallModel(startTimeMs = 1000)) assertThat((latest as OngoingActivityChipModel.Shown).colors) .isEqualTo(ColorsModel.Themed) @@ -158,7 +231,7 @@ class CallChipViewModelTest : SysuiTestCase() { testScope.runTest { val latest by collectLastValue(underTest.chip) - repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 0, intent = null)) + repo.setOngoingCallState(inCallModel(startTimeMs = 0)) assertThat((latest as OngoingActivityChipModel.Shown).colors) .isEqualTo(ColorsModel.Themed) @@ -172,7 +245,7 @@ class CallChipViewModelTest : SysuiTestCase() { kosmos.fakeSystemClock.setElapsedRealtime(400_000) // Start a call - repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 1000, intent = null)) + repo.setOngoingCallState(inCallModel(startTimeMs = 1000)) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) assertThat((latest as OngoingActivityChipModel.Shown.Timer).startTimeMs) .isEqualTo(398_000) @@ -186,7 +259,7 @@ class CallChipViewModelTest : SysuiTestCase() { kosmos.fakeSystemClock.setElapsedRealtime(500_000) // Start a new call, which started 1000ms ago - repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 102_000, intent = null)) + repo.setOngoingCallState(inCallModel(startTimeMs = 102_000)) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) assertThat((latest as OngoingActivityChipModel.Shown.Timer).startTimeMs) .isEqualTo(499_000) @@ -197,7 +270,7 @@ class CallChipViewModelTest : SysuiTestCase() { testScope.runTest { val latest by collectLastValue(underTest.chip) - repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 1000, intent = null)) + repo.setOngoingCallState(inCallModel(startTimeMs = 1000, intent = null)) assertThat((latest as OngoingActivityChipModel.Shown).onClickListener).isNull() } @@ -208,7 +281,7 @@ class CallChipViewModelTest : SysuiTestCase() { val latest by collectLastValue(underTest.chip) val intent = mock<PendingIntent>() - repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 1000, intent = intent)) + repo.setOngoingCallState(inCallModel(startTimeMs = 1000, intent = intent)) val clickListener = (latest as OngoingActivityChipModel.Shown).onClickListener assertThat(clickListener).isNotNull() @@ -223,7 +296,7 @@ class CallChipViewModelTest : SysuiTestCase() { val latest by collectLastValue(underTest.chip) val intent = mock<PendingIntent>() - repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 0, intent = intent)) + repo.setOngoingCallState(inCallModel(startTimeMs = 0, intent = intent)) val clickListener = (latest as OngoingActivityChipModel.Shown).onClickListener assertThat(clickListener).isNotNull() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt index 02764f8a15fd..a8d2c5b4cdd7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt @@ -125,8 +125,11 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() { ) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) - val icon = (latest as OngoingActivityChipModel.Shown).icon - assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_cast_connected) + val icon = + (((latest as OngoingActivityChipModel.Shown).icon) + as OngoingActivityChipModel.ChipIcon.Basic) + .impl as Icon.Resource + assertThat(icon.res).isEqualTo(R.drawable.ic_cast_connected) assertThat((icon.contentDescription as ContentDescription.Resource).res) .isEqualTo(R.string.cast_screen_to_other_device_chip_accessibility_label) } @@ -141,8 +144,11 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() { MediaProjectionState.Projecting.EntireScreen(CAST_TO_OTHER_DEVICES_PACKAGE) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) - val icon = (latest as OngoingActivityChipModel.Shown).icon - assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_cast_connected) + val icon = + (((latest as OngoingActivityChipModel.Shown).icon) + as OngoingActivityChipModel.ChipIcon.Basic) + .impl as Icon.Resource + assertThat(icon.res).isEqualTo(R.drawable.ic_cast_connected) assertThat((icon.contentDescription as ContentDescription.Resource).res) .isEqualTo(R.string.cast_screen_to_other_device_chip_accessibility_label) } @@ -176,8 +182,11 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() { ) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java) - val icon = (latest as OngoingActivityChipModel.Shown).icon - assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_cast_connected) + val icon = + (((latest as OngoingActivityChipModel.Shown).icon) + as OngoingActivityChipModel.ChipIcon.Basic) + .impl as Icon.Resource + assertThat(icon.res).isEqualTo(R.drawable.ic_cast_connected) // This content description is just generic "Casting", not "Casting screen" assertThat((icon.contentDescription as ContentDescription.Resource).res) .isEqualTo(R.string.accessibility_casting) @@ -203,8 +212,11 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() { // Only the projection info will show a timer assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) - val icon = (latest as OngoingActivityChipModel.Shown).icon - assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_cast_connected) + val icon = + (((latest as OngoingActivityChipModel.Shown).icon) + as OngoingActivityChipModel.ChipIcon.Basic) + .impl as Icon.Resource + assertThat(icon.res).isEqualTo(R.drawable.ic_cast_connected) // MediaProjection == screen casting, so this content description reflects that we're // using the MediaProjection information. assertThat((icon.contentDescription as ContentDescription.Resource).res) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt index b4a37ee1a55e..e68fa0bc6eb3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt @@ -148,8 +148,11 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() { screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) - val icon = (latest as OngoingActivityChipModel.Shown).icon - assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenrecord) + val icon = + (((latest as OngoingActivityChipModel.Shown).icon) + as OngoingActivityChipModel.ChipIcon.Basic) + .impl as Icon.Resource + assertThat(icon.res).isEqualTo(R.drawable.ic_screenrecord) assertThat(icon.contentDescription).isNotNull() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt index 2658679dee08..a2ef59916ff6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt @@ -133,8 +133,11 @@ class ShareToAppChipViewModelTest : SysuiTestCase() { ) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) - val icon = (latest as OngoingActivityChipModel.Shown).icon - assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_present_to_all) + val icon = + (((latest as OngoingActivityChipModel.Shown).icon) + as OngoingActivityChipModel.ChipIcon.Basic) + .impl as Icon.Resource + assertThat(icon.res).isEqualTo(R.drawable.ic_present_to_all) assertThat(icon.contentDescription).isNotNull() } @@ -147,8 +150,11 @@ class ShareToAppChipViewModelTest : SysuiTestCase() { MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) - val icon = (latest as OngoingActivityChipModel.Shown).icon - assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_present_to_all) + val icon = + (((latest as OngoingActivityChipModel.Shown).icon) + as OngoingActivityChipModel.ChipIcon.Basic) + .impl as Icon.Resource + assertThat(icon.res).isEqualTo(R.drawable.ic_present_to_all) assertThat(icon.contentDescription).isNotNull() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt index b9049e8f76b6..a724cfaa4798 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.chips.ui.viewmodel +import androidx.annotation.DrawableRes import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.Icon @@ -50,7 +51,7 @@ class ChipTransitionHelperTest : SysuiTestCase() { val newChip = OngoingActivityChipModel.Shown.Timer( - icon = Icon.Resource(R.drawable.ic_cake, contentDescription = null), + icon = createIcon(R.drawable.ic_cake), colors = ColorsModel.Themed, startTimeMs = 100L, onClickListener = null, @@ -62,7 +63,7 @@ class ChipTransitionHelperTest : SysuiTestCase() { val newerChip = OngoingActivityChipModel.Shown.IconOnly( - icon = Icon.Resource(R.drawable.ic_hotspot, contentDescription = null), + icon = createIcon(R.drawable.ic_hotspot), colors = ColorsModel.Themed, onClickListener = null, ) @@ -82,7 +83,7 @@ class ChipTransitionHelperTest : SysuiTestCase() { val shownChip = OngoingActivityChipModel.Shown.Timer( - icon = Icon.Resource(R.drawable.ic_cake, contentDescription = null), + icon = createIcon(R.drawable.ic_cake), colors = ColorsModel.Themed, startTimeMs = 100L, onClickListener = null, @@ -122,7 +123,7 @@ class ChipTransitionHelperTest : SysuiTestCase() { val shownChip = OngoingActivityChipModel.Shown.Timer( - icon = Icon.Resource(R.drawable.ic_cake, contentDescription = null), + icon = createIcon(R.drawable.ic_cake), colors = ColorsModel.Themed, startTimeMs = 100L, onClickListener = null, @@ -151,4 +152,7 @@ class ChipTransitionHelperTest : SysuiTestCase() { advanceTimeBy(2) assertThat(latest).isEqualTo(shownChip) } + + private fun createIcon(@DrawableRes drawable: Int) = + OngoingActivityChipModel.ChipIcon.Basic(Icon.Resource(drawable, contentDescription = null)) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt index ee249f0f8a2c..556ec6a307ab 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt @@ -42,6 +42,7 @@ import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory 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.util.time.fakeSystemClock import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -120,7 +121,7 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { testScope.runTest { screenRecordState.value = ScreenRecordModel.Recording - callRepo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 34, intent = null)) + callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) val latest by collectLastValue(underTest.chip) @@ -146,7 +147,7 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { screenRecordState.value = ScreenRecordModel.DoingNothing mediaProjectionState.value = MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) - callRepo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 34, intent = null)) + callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) val latest by collectLastValue(underTest.chip) @@ -160,7 +161,7 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { // MediaProjection covers both share-to-app and cast-to-other-device mediaProjectionState.value = MediaProjectionState.NotProjecting - callRepo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 34, intent = null)) + callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) val latest by collectLastValue(underTest.chip) @@ -171,7 +172,7 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { fun chip_higherPriorityChipAdded_lowerPriorityChipReplaced() = testScope.runTest { // Start with just the lower priority call chip - callRepo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 34, intent = null)) + callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) mediaProjectionState.value = MediaProjectionState.NotProjecting screenRecordState.value = ScreenRecordModel.DoingNothing @@ -205,7 +206,7 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { mediaProjectionState.value = MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) - callRepo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 34, intent = null)) + callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) val latest by collectLastValue(underTest.chip) @@ -335,21 +336,29 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { fun assertIsScreenRecordChip(latest: OngoingActivityChipModel?) { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) - val icon = (latest as OngoingActivityChipModel.Shown).icon - assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenrecord) + val icon = + (((latest as OngoingActivityChipModel.Shown).icon) + as OngoingActivityChipModel.ChipIcon.Basic) + .impl as Icon.Resource + assertThat(icon.res).isEqualTo(R.drawable.ic_screenrecord) } fun assertIsShareToAppChip(latest: OngoingActivityChipModel?) { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) - val icon = (latest as OngoingActivityChipModel.Shown).icon - assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_present_to_all) + val icon = + (((latest as OngoingActivityChipModel.Shown).icon) + as OngoingActivityChipModel.ChipIcon.Basic) + .impl as Icon.Resource + assertThat(icon.res).isEqualTo(R.drawable.ic_present_to_all) } fun assertIsCallChip(latest: OngoingActivityChipModel?) { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) - val icon = (latest as OngoingActivityChipModel.Shown).icon - assertThat((icon as Icon.Resource).res) - .isEqualTo(com.android.internal.R.drawable.ic_phone) + val icon = + (((latest as OngoingActivityChipModel.Shown).icon) + as OngoingActivityChipModel.ChipIcon.Basic) + .impl as Icon.Resource + assertThat(icon.res).isEqualTo(com.android.internal.R.drawable.ic_phone) } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt index 6a5976eccd3a..48ae7a2aa260 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt @@ -39,6 +39,7 @@ import com.android.systemui.statusbar.phone.StatusBarBoundsProvider import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent 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.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.capture @@ -396,9 +397,7 @@ class StatusBarModeRepositoryImplTest : SysuiTestCase() { testScope.runTest { val latest by collectLastValue(underTest.statusBarAppearance) - ongoingCallRepository.setOngoingCallState( - OngoingCallModel.InCall(startTimeMs = 34, intent = null) - ) + ongoingCallRepository.setOngoingCallState(inCallModel(startTimeMs = 34)) onSystemBarAttributesChanged( requestedVisibleTypes = WindowInsets.Type.navigationBars(), ) @@ -411,9 +410,8 @@ class StatusBarModeRepositoryImplTest : SysuiTestCase() { testScope.runTest { val latest by collectLastValue(underTest.statusBarAppearance) - ongoingCallRepository.setOngoingCallState( - OngoingCallModel.InCall(startTimeMs = 789, intent = null) - ) + ongoingCallRepository.setOngoingCallState(inCallModel(startTimeMs = 789)) + onSystemBarAttributesChanged( requestedVisibleTypes = WindowInsets.Type.statusBars(), appearance = APPEARANCE_OPAQUE_STATUS_BARS, 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 bfa816e65eb2..25138fd0ff83 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 @@ -30,9 +30,12 @@ import android.graphics.drawable.Icon import android.os.Bundle import android.os.SystemClock import android.os.UserHandle +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags 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.notification.collection.NotificationEntry @@ -108,6 +111,28 @@ class IconManagerTest : SysuiTestCase() { } @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) + fun testCreateIcons_chipNotifIconFlagEnabled_statusBarChipIconIsNull() { + val entry = + notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = true) + entry?.let { iconManager.createIcons(it) } + testScope.runCurrent() + + assertThat(entry?.icons?.statusBarChipIcon).isNotNull() + } + + @Test fun testCreateIcons_importantConversation_shortcutIcon() { val entry = notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = true) @@ -179,6 +204,7 @@ class IconManagerTest : SysuiTestCase() { } @Test + @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON) fun testCreateIcons_sensitiveImportantConversation() { val entry = notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = false) @@ -187,11 +213,13 @@ class IconManagerTest : SysuiTestCase() { entry?.let { iconManager.createIcons(it) } testScope.runCurrent() assertThat(entry?.icons?.statusBarIcon?.sourceIcon).isEqualTo(shortcutIc) + assertThat(entry?.icons?.statusBarChipIcon?.sourceIcon).isEqualTo(shortcutIc) assertThat(entry?.icons?.shelfIcon?.sourceIcon).isEqualTo(smallIc) assertThat(entry?.icons?.aodIcon?.sourceIcon).isEqualTo(smallIc) } @Test + @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON) fun testUpdateIcons_sensitiveImportantConversation() { val entry = notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = false) @@ -202,6 +230,7 @@ class IconManagerTest : SysuiTestCase() { entry?.let { iconManager.updateIcons(it) } testScope.runCurrent() assertThat(entry?.icons?.statusBarIcon?.sourceIcon).isEqualTo(shortcutIc) + assertThat(entry?.icons?.statusBarChipIcon?.sourceIcon).isEqualTo(shortcutIc) assertThat(entry?.icons?.shelfIcon?.sourceIcon).isEqualTo(smallIc) assertThat(entry?.icons?.aodIcon?.sourceIcon).isEqualTo(smallIc) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt index 6c2e2c6ef47d..dfe01bf45f38 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt @@ -28,7 +28,7 @@ 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 +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 @@ -39,6 +39,7 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.log.logcatLogBuffer import com.android.systemui.plugins.activityStarter import com.android.systemui.res.R +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 @@ -160,6 +161,47 @@ class OngoingCallControllerViaRepoTest : SysuiTestCase() { } @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() = + 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).isEqualTo(icon) + } + + @Test fun notifRepoHasOngoingCallNotif_isOngoingCallNotif_windowControllerUpdated() { setCallNotifOnRepo() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt index cbb8fe82eff1..4c6eaa589e6a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt @@ -21,6 +21,7 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.kosmos.Kosmos import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel +import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith @@ -33,7 +34,7 @@ class OngoingCallRepositoryTest : SysuiTestCase() { @Test fun hasOngoingCall_matchesSet() { - val inCallModel = OngoingCallModel.InCall(startTimeMs = 654, intent = null) + val inCallModel = inCallModel(startTimeMs = 654) underTest.setOngoingCallState(inCallModel) assertThat(underTest.ongoingCallState.value).isEqualTo(inCallModel) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt index 37f1f137094e..76bdc0de3d7b 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.data.model import android.app.PendingIntent import android.graphics.drawable.Icon +import com.android.systemui.statusbar.StatusBarIconView import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel import com.android.systemui.statusbar.notification.shared.CallType import com.android.systemui.statusbar.notification.stack.BUCKET_UNKNOWN @@ -36,6 +37,7 @@ fun activeNotificationModel( aodIcon: Icon? = null, shelfIcon: Icon? = null, statusBarIcon: Icon? = null, + statusBarChipIcon: StatusBarIconView? = null, uid: Int = 0, instanceId: Int? = null, isGroupSummary: Boolean = false, @@ -57,6 +59,7 @@ fun activeNotificationModel( aodIcon = aodIcon, shelfIcon = shelfIcon, statusBarIcon = statusBarIcon, + statusBarChipIconView = statusBarChipIcon, uid = uid, packageName = packageName, contentIntent = contentIntent, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModelBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModelBuilder.kt new file mode 100644 index 000000000000..3963d7c5be63 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModelBuilder.kt @@ -0,0 +1,27 @@ +/* + * 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.shared.model + +import android.app.PendingIntent +import com.android.systemui.statusbar.StatusBarIconView + +/** Helper for building [OngoingCallModel.InCall] instances in tests. */ +fun inCallModel( + startTimeMs: Long, + notificationIcon: StatusBarIconView? = null, + intent: PendingIntent? = null +) = OngoingCallModel.InCall(startTimeMs, notificationIcon, intent) |