diff options
14 files changed, 1042 insertions, 11 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index f73a602bda63..64c4eec3b98a 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -107,6 +107,7 @@ import com.android.systemui.statusbar.notification.row.dagger.NotificationRowCom import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent; import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.LetterboxModule; +import com.android.systemui.statusbar.phone.NotificationIconAreaControllerModule; import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent; import com.android.systemui.statusbar.pipeline.dagger.StatusBarPipelineModule; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -184,6 +185,7 @@ import javax.inject.Named; MediaProjectionModule.class, MediaProjectionTaskSwitcherModule.class, MotionToolModule.class, + NotificationIconAreaControllerModule.class, PeopleHubModule.class, PeopleModule.class, PluginModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt new file mode 100644 index 000000000000..26dfe3edf793 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt @@ -0,0 +1,684 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.notification.icon.ui.viewbinder + +import android.content.Context +import android.graphics.Color +import android.graphics.Rect +import android.os.Bundle +import android.os.Trace +import android.view.LayoutInflater +import android.view.View +import android.widget.FrameLayout +import androidx.annotation.ColorInt +import androidx.annotation.VisibleForTesting +import androidx.collection.ArrayMap +import com.android.app.animation.Interpolators +import com.android.internal.statusbar.StatusBarIcon +import com.android.internal.util.ContrastColorUtil +import com.android.settingslib.Utils +import com.android.systemui.R +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.demomode.DemoMode +import com.android.systemui.demomode.DemoModeController +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.flags.ViewRefactorFlag +import com.android.systemui.plugins.DarkIconDispatcher +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.statusbar.CrossFadeHelper +import com.android.systemui.statusbar.NotificationListener +import com.android.systemui.statusbar.NotificationMediaManager +import com.android.systemui.statusbar.NotificationShelfController +import com.android.systemui.statusbar.StatusBarIconView +import com.android.systemui.statusbar.StatusBarState +import com.android.systemui.statusbar.notification.NotificationUtils +import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator +import com.android.systemui.statusbar.notification.collection.ListEntry +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerShelfViewModel +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel +import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinderWrapperControllerImpl +import com.android.systemui.statusbar.phone.DozeParameters +import com.android.systemui.statusbar.phone.KeyguardBypassController +import com.android.systemui.statusbar.phone.NotificationIconAreaController +import com.android.systemui.statusbar.phone.NotificationIconContainer +import com.android.systemui.statusbar.phone.ScreenOffAnimationController +import com.android.systemui.statusbar.window.StatusBarWindowController +import com.android.wm.shell.bubbles.Bubbles +import java.util.Optional +import java.util.function.Function +import javax.inject.Inject +import kotlinx.coroutines.DisposableHandle + +/** + * Controller class for [NotificationIconContainer]. This implementation serves as a temporary + * wrapper around [NotificationIconContainerViewBinder], so that external code can continue to + * depend on the [NotificationIconAreaController] interface. Once + * [LegacyNotificationIconAreaControllerImpl] is removed, this class can go away and the ViewBinder + * can be used directly. + */ +@SysUISingleton +class NotificationIconAreaControllerViewBinderWrapperImpl +@Inject +constructor( + private val context: Context, + private val statusBarStateController: StatusBarStateController, + private val wakeUpCoordinator: NotificationWakeUpCoordinator, + private val bypassController: KeyguardBypassController, + private val mediaManager: NotificationMediaManager, + notificationListener: NotificationListener, + private val dozeParameters: DozeParameters, + private val sectionStyleProvider: SectionStyleProvider, + private val bubblesOptional: Optional<Bubbles>, + demoModeController: DemoModeController, + darkIconDispatcher: DarkIconDispatcher, + featureFlags: FeatureFlags, + private val statusBarWindowController: StatusBarWindowController, + private val screenOffAnimationController: ScreenOffAnimationController, + private val shelfIconsViewModel: NotificationIconContainerShelfViewModel, + private val statusBarIconsViewModel: NotificationIconContainerStatusBarViewModel, + private val aodIconsViewModel: NotificationIconContainerAlwaysOnDisplayViewModel, +) : + NotificationIconAreaController, + DarkIconDispatcher.DarkReceiver, + StatusBarStateController.StateListener, + NotificationWakeUpCoordinator.WakeUpListener, + DemoMode { + + private val contrastColorUtil: ContrastColorUtil = ContrastColorUtil.getInstance(context) + private val updateStatusBarIcons = Runnable { updateStatusBarIcons() } + private val shelfRefactor = ViewRefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR) + private val tintAreas = ArrayList<Rect>() + + private var iconSize = 0 + private var iconHPadding = 0 + private var iconTint = Color.WHITE + private var notificationEntries = listOf<ListEntry>() + private var notificationIconArea: View? = null + private var notificationIcons: NotificationIconContainer? = null + private var shelfIcons: NotificationIconContainer? = null + private var aodIcons: NotificationIconContainer? = null + private var aodBindJob: DisposableHandle? = null + private var aodIconAppearTranslation = 0 + private var animationsEnabled = false + private var aodIconTint = 0 + private var aodIconsVisible = false + private var showLowPriority = true + + @VisibleForTesting + val settingsListener: NotificationListener.NotificationSettingsListener = + object : NotificationListener.NotificationSettingsListener { + override fun onStatusBarIconsBehaviorChanged(hideSilentStatusIcons: Boolean) { + showLowPriority = !hideSilentStatusIcons + updateStatusBarIcons() + } + } + + init { + statusBarStateController.addCallback(this) + wakeUpCoordinator.addListener(this) + demoModeController.addCallback(this) + notificationListener.addNotificationSettingsListener(settingsListener) + initializeNotificationAreaViews(context) + reloadAodColor() + darkIconDispatcher.addDarkReceiver(this) + } + + @VisibleForTesting + fun shouldShowLowPriorityIcons(): Boolean { + return showLowPriority + } + + /** Called by the Keyguard*ViewController whose view contains the aod icons. */ + override fun setupAodIcons(aodIcons: NotificationIconContainer) { + val changed = this.aodIcons != null && aodIcons !== this.aodIcons + if (changed) { + this.aodIcons!!.setAnimationsEnabled(false) + this.aodIcons!!.removeAllViews() + aodBindJob?.dispose() + } + this.aodIcons = aodIcons + this.aodIcons!!.setOnLockScreen(true) + aodBindJob = NotificationIconContainerViewBinder.bind(aodIcons, aodIconsViewModel) + updateAodIconsVisibility(animate = false, forceUpdate = changed) + updateAnimations() + if (changed) { + updateAodNotificationIcons() + } + updateIconLayoutParams(context) + } + + override fun setupShelf(notificationShelfController: NotificationShelfController) = + NotificationShelfViewBinderWrapperControllerImpl.unsupported + + override fun setShelfIcons(icons: NotificationIconContainer) { + if (shelfRefactor.expectEnabled()) { + NotificationIconContainerViewBinder.bind(icons, shelfIconsViewModel) + shelfIcons = icons + } + } + + override fun onDensityOrFontScaleChanged(context: Context) { + updateIconLayoutParams(context) + } + + /** Returns the view that represents the notification area. */ + override fun getNotificationInnerAreaView(): View? { + return notificationIconArea + } + + /** + * See [com.android.systemui.statusbar.policy.DarkIconDispatcher.setIconsDarkArea]. Sets the + * color that should be used to tint any icons in the notification area. + * + * @param tintAreas the areas in which to tint the icons, specified in screen coordinates + * @param darkIntensity + */ + override fun onDarkChanged(tintAreas: ArrayList<Rect>, darkIntensity: Float, iconTint: Int) { + this.tintAreas.clear() + this.tintAreas.addAll(tintAreas) + if (DarkIconDispatcher.isInAreas(tintAreas, notificationIconArea)) { + this.iconTint = iconTint + } + applyNotificationIconsTint() + } + + /** Updates the notifications with the given list of notifications to display. */ + override fun updateNotificationIcons(entries: List<ListEntry>) { + notificationEntries = entries + updateNotificationIcons() + } + + private fun updateStatusBarIcons() { + updateIconsForLayout( + { entry: NotificationEntry -> entry.icons.statusBarIcon }, + notificationIcons, + showAmbient = false /* showAmbient */, + showLowPriority = showLowPriority, + hideDismissed = true /* hideDismissed */, + hideRepliedMessages = true /* hideRepliedMessages */, + hideCurrentMedia = false /* hideCurrentMedia */, + hidePulsing = false /* hidePulsing */ + ) + } + + override fun updateAodNotificationIcons() { + if (aodIcons == null) { + return + } + updateIconsForLayout( + { entry: NotificationEntry -> entry.icons.aodIcon }, + aodIcons, + showAmbient = false /* showAmbient */, + showLowPriority = true /* showLowPriority */, + hideDismissed = true /* hideDismissed */, + hideRepliedMessages = true /* hideRepliedMessages */, + hideCurrentMedia = true /* hideCurrentMedia */, + hidePulsing = bypassController.bypassEnabled /* hidePulsing */ + ) + } + + override fun showIconIsolated(icon: StatusBarIconView?, animated: Boolean) { + notificationIcons!!.showIconIsolated(icon, animated) + } + + override fun setIsolatedIconLocation(iconDrawingRect: Rect, requireStateUpdate: Boolean) { + notificationIcons!!.setIsolatedIconLocation(iconDrawingRect, requireStateUpdate) + } + + override fun onDozingChanged(isDozing: Boolean) { + if (aodIcons == null) { + return + } + val animate = (dozeParameters.alwaysOn && !dozeParameters.displayNeedsBlanking) + aodIcons!!.setDozing(isDozing, animate, 0) + } + + override fun setAnimationsEnabled(enabled: Boolean) { + animationsEnabled = enabled + updateAnimations() + } + + override fun onStateChanged(newState: Int) { + updateAodIconsVisibility(animate = false, forceUpdate = false) + updateAnimations() + } + + override fun onThemeChanged() { + reloadAodColor() + updateAodIconColors() + } + + override fun getHeight(): Int { + return if (aodIcons == null) 0 else aodIcons!!.height + } + + @VisibleForTesting + fun appearAodIcons() { + if (aodIcons == null) { + return + } + if (screenOffAnimationController.shouldAnimateAodIcons()) { + aodIcons!!.translationY = -aodIconAppearTranslation.toFloat() + aodIcons!!.alpha = 0f + animateInAodIconTranslation() + aodIcons!! + .animate() + .alpha(1f) + .setInterpolator(Interpolators.LINEAR) + .setDuration(AOD_ICONS_APPEAR_DURATION) + .start() + } else { + aodIcons!!.alpha = 1.0f + aodIcons!!.translationY = 0f + } + } + + override fun onFullyHiddenChanged(isFullyHidden: Boolean) { + var animate = true + if (!bypassController.bypassEnabled) { + animate = dozeParameters.alwaysOn && !dozeParameters.displayNeedsBlanking + // We only want the appear animations to happen when the notifications get fully hidden, + // since otherwise the unhide animation overlaps + animate = animate and isFullyHidden + } + updateAodIconsVisibility(animate, false /* force */) + updateAodNotificationIcons() + updateAodIconColors() + } + + override fun onPulseExpansionChanged(expandingChanged: Boolean) { + if (expandingChanged) { + updateAodIconsVisibility(animate = true, forceUpdate = false) + } + } + + override fun demoCommands(): List<String> { + val commands = ArrayList<String>() + commands.add(DemoMode.COMMAND_NOTIFICATIONS) + return commands + } + + override fun dispatchDemoCommand(command: String, args: Bundle) { + if (notificationIconArea != null) { + val visible = args.getString("visible") + val vis = if ("false" == visible) View.INVISIBLE else View.VISIBLE + notificationIconArea?.visibility = vis + } + } + + override fun onDemoModeFinished() { + if (notificationIconArea != null) { + notificationIconArea?.visibility = View.VISIBLE + } + } + + private fun inflateIconArea(inflater: LayoutInflater): View { + return inflater.inflate(R.layout.notification_icon_area, null) + } + + /** Initializes the views that will represent the notification area. */ + private fun initializeNotificationAreaViews(context: Context) { + reloadDimens(context) + val layoutInflater = LayoutInflater.from(context) + notificationIconArea = inflateIconArea(layoutInflater) + notificationIcons = notificationIconArea?.findViewById(R.id.notificationIcons) + NotificationIconContainerViewBinder.bind(notificationIcons!!, statusBarIconsViewModel) + } + + private fun updateIconLayoutParams(context: Context) { + reloadDimens(context) + val params = generateIconLayoutParams() + for (i in 0 until notificationIcons!!.childCount) { + val child = notificationIcons!!.getChildAt(i) + child.layoutParams = params + } + if (shelfIcons != null) { + for (i in 0 until shelfIcons!!.childCount) { + val child = shelfIcons!!.getChildAt(i) + child.layoutParams = params + } + } + if (aodIcons != null) { + for (i in 0 until aodIcons!!.childCount) { + val child = aodIcons!!.getChildAt(i) + child.layoutParams = params + } + } + } + + private fun generateIconLayoutParams(): FrameLayout.LayoutParams { + return FrameLayout.LayoutParams( + iconSize + 2 * iconHPadding, + statusBarWindowController.statusBarHeight + ) + } + + private fun reloadDimens(context: Context) { + val res = context.resources + iconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size_sp) + iconHPadding = res.getDimensionPixelSize(R.dimen.status_bar_icon_horizontal_margin) + aodIconAppearTranslation = res.getDimensionPixelSize(R.dimen.shelf_appear_translation) + } + + private fun shouldShowNotificationIcon( + entry: NotificationEntry, + showAmbient: Boolean, + showLowPriority: Boolean, + hideDismissed: Boolean, + hideRepliedMessages: Boolean, + hideCurrentMedia: Boolean, + hidePulsing: Boolean + ): Boolean { + if (!showAmbient && sectionStyleProvider.isMinimized(entry)) { + return false + } + if (hideCurrentMedia && entry.key == mediaManager.mediaNotificationKey) { + return false + } + if (!showLowPriority && sectionStyleProvider.isSilent(entry)) { + return false + } + if (entry.isRowDismissed && hideDismissed) { + return false + } + if (hideRepliedMessages && entry.isLastMessageFromReply) { + return false + } + // showAmbient == show in shade but not shelf + if (!showAmbient && entry.shouldSuppressStatusBar()) { + return false + } + if ( + hidePulsing && + entry.showingPulsing() && + (!wakeUpCoordinator.notificationsFullyHidden || !entry.isPulseSuppressed) + ) { + return false + } + return if (bubblesOptional.isPresent && bubblesOptional.get().isBubbleExpanded(entry.key)) { + false + } else true + } + + private fun updateNotificationIcons() { + Trace.beginSection("NotificationIconAreaController.updateNotificationIcons") + updateStatusBarIcons() + updateShelfIcons() + updateAodNotificationIcons() + applyNotificationIconsTint() + Trace.endSection() + } + + private fun updateShelfIcons() { + if (shelfIcons == null) { + return + } + updateIconsForLayout( + { entry: NotificationEntry -> entry.icons.shelfIcon }, + shelfIcons, + showAmbient = true, + showLowPriority = true, + hideDismissed = false, + hideRepliedMessages = false, + hideCurrentMedia = false, + hidePulsing = false + ) + } + + /** + * Updates the notification icons for a host layout. This will ensure that the notification host + * layout will have the same icons like the ones in here. + * + * @param function A function to look up an icon view based on an entry + * @param hostLayout which layout should be updated + * @param showAmbient should ambient notification icons be shown + * @param showLowPriority should icons from silent notifications be shown + * @param hideDismissed should dismissed icons be hidden + * @param hideRepliedMessages should messages that have been replied to be hidden + * @param hidePulsing should pulsing notifications be hidden + */ + private fun updateIconsForLayout( + function: Function<NotificationEntry, StatusBarIconView?>, + hostLayout: NotificationIconContainer?, + showAmbient: Boolean, + showLowPriority: Boolean, + hideDismissed: Boolean, + hideRepliedMessages: Boolean, + hideCurrentMedia: Boolean, + hidePulsing: Boolean, + ) { + val toShow = ArrayList<StatusBarIconView>(notificationEntries.size) + // Filter out ambient notifications and notification children. + for (i in notificationEntries.indices) { + val entry = notificationEntries[i].representativeEntry + if (entry != null && entry.row != null) { + if ( + shouldShowNotificationIcon( + entry, + showAmbient, + showLowPriority, + hideDismissed, + hideRepliedMessages, + hideCurrentMedia, + hidePulsing + ) + ) { + val iconView = function.apply(entry) + if (iconView != null) { + toShow.add(iconView) + } + } + } + } + + // In case we are changing the suppression of a group, the replacement shouldn't flicker + // and it should just be replaced instead. We therefore look for notifications that were + // just replaced by the child or vice-versa to suppress this. + val replacingIcons = ArrayMap<String, ArrayList<StatusBarIcon>>() + val toRemove = ArrayList<View>() + for (i in 0 until hostLayout!!.childCount) { + val child = hostLayout.getChildAt(i) as? StatusBarIconView ?: continue + if (!toShow.contains(child)) { + var iconWasReplaced = false + val removedGroupKey = child.notification.groupKey + for (j in toShow.indices) { + val candidate = toShow[j] + if ( + candidate.sourceIcon.sameAs(child.sourceIcon) && + candidate.notification.groupKey == removedGroupKey + ) { + if (!iconWasReplaced) { + iconWasReplaced = true + } else { + iconWasReplaced = false + break + } + } + } + if (iconWasReplaced) { + var statusBarIcons = replacingIcons[removedGroupKey] + if (statusBarIcons == null) { + statusBarIcons = ArrayList() + replacingIcons[removedGroupKey] = statusBarIcons + } + statusBarIcons.add(child.statusBarIcon) + } + toRemove.add(child) + } + } + // removing all duplicates + val duplicates = ArrayList<String?>() + for (key in replacingIcons.keys) { + val statusBarIcons = replacingIcons[key]!! + if (statusBarIcons.size != 1) { + duplicates.add(key) + } + } + replacingIcons.removeAll(duplicates) + hostLayout.setReplacingIcons(replacingIcons) + val toRemoveCount = toRemove.size + for (i in 0 until toRemoveCount) { + hostLayout.removeView(toRemove[i]) + } + val params = generateIconLayoutParams() + for (i in toShow.indices) { + val v = toShow[i] + // The view might still be transiently added if it was just removed and added again + hostLayout.removeTransientView(v) + if (v.parent == null) { + if (hideDismissed) { + v.setOnDismissListener(updateStatusBarIcons) + } + hostLayout.addView(v, i, params) + } + } + hostLayout.setChangingViewPositions(true) + // Re-sort notification icons + val childCount = hostLayout.childCount + for (i in 0 until childCount) { + val actual = hostLayout.getChildAt(i) + val expected = toShow[i] + if (actual === expected) { + continue + } + hostLayout.removeView(expected) + hostLayout.addView(expected, i) + } + hostLayout.setChangingViewPositions(false) + hostLayout.setReplacingIcons(null) + } + + /** Applies [.mIconTint] to the notification icons. */ + private fun applyNotificationIconsTint() { + for (i in 0 until notificationIcons!!.childCount) { + val iv = notificationIcons!!.getChildAt(i) as StatusBarIconView + if (iv.width != 0) { + updateTintForIcon(iv, iconTint) + } else { + iv.executeOnLayout { updateTintForIcon(iv, iconTint) } + } + } + updateAodIconColors() + } + + private fun updateTintForIcon(v: StatusBarIconView, tint: Int) { + val isPreL = java.lang.Boolean.TRUE == v.getTag(R.id.icon_is_pre_L) + var color = StatusBarIconView.NO_COLOR + val colorize = !isPreL || NotificationUtils.isGrayscale(v, contrastColorUtil) + if (colorize) { + color = DarkIconDispatcher.getTint(tintAreas, v, tint) + } + v.staticDrawableColor = color + v.setDecorColor(tint) + } + + private fun updateAnimations() { + val inShade = statusBarStateController.state == StatusBarState.SHADE + if (aodIcons != null) { + aodIcons!!.setAnimationsEnabled(animationsEnabled && !inShade) + } + notificationIcons!!.setAnimationsEnabled(animationsEnabled && inShade) + } + + private fun animateInAodIconTranslation() { + aodIcons!! + .animate() + .setInterpolator(Interpolators.DECELERATE_QUINT) + .translationY(0f) + .setDuration(AOD_ICONS_APPEAR_DURATION) + .start() + } + + private fun reloadAodColor() { + aodIconTint = + Utils.getColorAttrDefaultColor( + context, + R.attr.wallpaperTextColor, + DEFAULT_AOD_ICON_COLOR + ) + } + + private fun updateAodIconColors() { + if (aodIcons != null) { + for (i in 0 until aodIcons!!.childCount) { + val iv = aodIcons!!.getChildAt(i) as StatusBarIconView + if (iv.width != 0) { + updateTintForIcon(iv, aodIconTint) + } else { + iv.executeOnLayout { updateTintForIcon(iv, aodIconTint) } + } + } + } + } + + private fun updateAodIconsVisibility(animate: Boolean, forceUpdate: Boolean) { + if (aodIcons == null) { + return + } + var visible = (bypassController.bypassEnabled || wakeUpCoordinator.notificationsFullyHidden) + + // Hide the AOD icons if we're not in the KEYGUARD state unless the screen off animation is + // playing, in which case we want them to be visible since we're animating in the AOD UI and + // will be switching to KEYGUARD shortly. + if ( + statusBarStateController.state != StatusBarState.KEYGUARD && + !screenOffAnimationController.shouldShowAodIconsWhenShade() + ) { + visible = false + } + if (visible && wakeUpCoordinator.isPulseExpanding() && !bypassController.bypassEnabled) { + visible = false + } + if (aodIconsVisible != visible || forceUpdate) { + aodIconsVisible = visible + aodIcons!!.animate().cancel() + if (animate) { + val wasFullyInvisible = aodIcons!!.visibility != View.VISIBLE + if (aodIconsVisible) { + if (wasFullyInvisible) { + // No fading here, let's just appear the icons instead! + aodIcons!!.visibility = View.VISIBLE + aodIcons!!.alpha = 1.0f + appearAodIcons() + } else { + // Let's make sure the icon are translated to 0, since we cancelled it above + animateInAodIconTranslation() + // We were fading out, let's fade in instead + CrossFadeHelper.fadeIn(aodIcons) + } + } else { + // Let's make sure the icon are translated to 0, since we cancelled it above + animateInAodIconTranslation() + CrossFadeHelper.fadeOut(aodIcons) + } + } else { + aodIcons!!.alpha = 1.0f + aodIcons!!.translationY = 0f + aodIcons!!.visibility = if (visible) View.VISIBLE else View.INVISIBLE + } + } + } + + companion object { + private const val AOD_ICONS_APPEAR_DURATION: Long = 200 + + @ColorInt private val DEFAULT_AOD_ICON_COLOR = -0x1 + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt new file mode 100644 index 000000000000..8293bb329a01 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.notification.icon.ui.viewbinder + +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.repeatOnLifecycle +import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel +import com.android.systemui.statusbar.phone.NotificationIconContainer +import kotlinx.coroutines.DisposableHandle + +/** Binds a [NotificationIconContainer] to its [view model][NotificationIconContainerViewModel]. */ +object NotificationIconContainerViewBinder { + fun bind( + view: NotificationIconContainer, + viewModel: NotificationIconContainerViewModel, + ): DisposableHandle { + return view.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.CREATED) {} } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt new file mode 100644 index 000000000000..f68b0ef79638 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.notification.icon.ui.viewmodel + +import javax.inject.Inject + +/** View-model for the row of notification icons displayed on the always-on display. */ +class NotificationIconContainerAlwaysOnDisplayViewModel @Inject constructor() : + NotificationIconContainerViewModel diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt new file mode 100644 index 000000000000..933c76f19aee --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.notification.icon.ui.viewmodel + +import javax.inject.Inject + +/** View-model for the overflow row of notification icons displayed in the notification shade. */ +class NotificationIconContainerShelfViewModel @Inject constructor() : + NotificationIconContainerViewModel diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt new file mode 100644 index 000000000000..2217646e6022 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.notification.icon.ui.viewmodel + +import javax.inject.Inject + +/** View-model for the row of notification icons displayed in the status bar, */ +class NotificationIconContainerStatusBarViewModel @Inject constructor() : + NotificationIconContainerViewModel diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerViewModel.kt new file mode 100644 index 000000000000..892b2be9ed6e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerViewModel.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.notification.icon.ui.viewmodel + +/** + * View-model for the row of notification icons displayed in the NotificationShelf, StatusBar, and + * AOD. + */ +interface NotificationIconContainerViewModel diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt index 22a87a7c9432..b92c51fac5f6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt @@ -64,8 +64,10 @@ class NotificationShelfViewBinderWrapperControllerImpl @Inject constructor() : override fun setOnClickListener(listener: View.OnClickListener) = unsupported - private val unsupported: Nothing - get() = error("Code path not supported when NOTIFICATION_SHELF_REFACTOR is disabled") + companion object { + val unsupported: Nothing + get() = error("Code path not supported when NOTIFICATION_SHELF_REFACTOR is disabled") + } } /** Binds a [NotificationShelf] to its [view model][NotificationShelfViewModel]. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 4668aa433533..6db8df91edcc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -28,7 +28,6 @@ import static com.android.systemui.statusbar.notification.stack.NotificationStac import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_HIGH_PRIORITY; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.SelectedRows; -import static com.android.systemui.statusbar.phone.NotificationIconAreaController.HIGH_PRIORITY; import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; import android.content.res.Configuration; @@ -150,6 +149,7 @@ import javax.inject.Named; public class NotificationStackScrollLayoutController { private static final String TAG = "StackScrollerController"; private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG); + private static final String HIGH_PRIORITY = "high_priority"; private final boolean mAllowLongPress; private final NotificationGutsManager mNotificationGutsManager; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java index 0bf0f4b504b0..d22ed3802eed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2023 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; import android.content.Context; @@ -43,6 +58,8 @@ import com.android.systemui.statusbar.notification.collection.provider.SectionSt import com.android.systemui.statusbar.window.StatusBarWindowController; import com.android.wm.shell.bubbles.Bubbles; +import org.jetbrains.annotations.NotNull; + import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -55,13 +72,13 @@ import javax.inject.Inject; * normally reserved for notifications. */ @SysUISingleton -public class NotificationIconAreaController implements +public class LegacyNotificationIconAreaControllerImpl implements + NotificationIconAreaController, DarkReceiver, StatusBarStateController.StateListener, NotificationWakeUpCoordinator.WakeUpListener, DemoMode { - public static final String HIGH_PRIORITY = "high_priority"; private static final long AOD_ICONS_APPEAR_DURATION = 200; @ColorInt private static final int DEFAULT_AOD_ICON_COLOR = 0xffffffff; @@ -110,7 +127,7 @@ public class NotificationIconAreaController implements }; @Inject - public NotificationIconAreaController( + public LegacyNotificationIconAreaControllerImpl( Context context, StatusBarStateController statusBarStateController, NotificationWakeUpCoordinator wakeUpCoordinator, @@ -192,7 +209,7 @@ public class NotificationIconAreaController implements } } - public void onDensityOrFontScaleChanged(Context context) { + public void onDensityOrFontScaleChanged(@NotNull Context context) { updateIconLayoutParams(context); } @@ -493,7 +510,7 @@ public class NotificationIconAreaController implements mNotificationIcons.showIconIsolated(icon, animated); } - public void setIsolatedIconLocation(Rect iconDrawingRect, boolean requireStateUpdate) { + public void setIsolatedIconLocation(@NotNull Rect iconDrawingRect, boolean requireStateUpdate) { mNotificationIcons.setIsolatedIconLocation(iconDrawingRect, requireStateUpdate); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.kt new file mode 100644 index 000000000000..0079f7ceb539 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.kt @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2023 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 + +import android.content.Context +import android.graphics.Rect +import android.view.View +import com.android.systemui.statusbar.NotificationShelfController +import com.android.systemui.statusbar.StatusBarIconView +import com.android.systemui.statusbar.notification.collection.ListEntry + +/** + * A controller for the space in the status bar to the left of the system icons. This area is + * normally reserved for notifications. + */ +interface NotificationIconAreaController { + /** Called by the Keyguard*ViewController whose view contains the aod icons. */ + fun setupAodIcons(aodIcons: NotificationIconContainer) + fun setupShelf(notificationShelfController: NotificationShelfController) + fun setShelfIcons(icons: NotificationIconContainer) + fun onDensityOrFontScaleChanged(context: Context) + + /** Returns the view that represents the notification area. */ + fun getNotificationInnerAreaView(): View? + + /** Updates the notifications with the given list of notifications to display. */ + fun updateNotificationIcons(entries: List<@JvmSuppressWildcards ListEntry>) + fun updateAodNotificationIcons() + fun showIconIsolated(icon: StatusBarIconView?, animated: Boolean) + fun setIsolatedIconLocation(iconDrawingRect: Rect, requireStateUpdate: Boolean) + fun setAnimationsEnabled(enabled: Boolean) + fun onThemeChanged() + fun getHeight(): Int +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerModule.kt new file mode 100644 index 000000000000..d1ddd51a04c7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerModule.kt @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2023 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 + +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconAreaControllerViewBinderWrapperImpl +import dagger.Module +import dagger.Provides +import javax.inject.Provider + +@Module +object NotificationIconAreaControllerModule { + @Provides + fun provideNotificationIconAreaControllerImpl( + featureFlags: FeatureFlags, + legacyProvider: Provider<LegacyNotificationIconAreaControllerImpl>, + newProvider: Provider<NotificationIconAreaControllerViewBinderWrapperImpl>, + ): NotificationIconAreaController = + if (featureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) { + newProvider.get() + } else { + legacyProvider.get() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImplTest.kt new file mode 100644 index 000000000000..b8792a8aeacd --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImplTest.kt @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.notification.icon.ui.viewbinder + +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.demomode.DemoModeController +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.plugins.DarkIconDispatcher +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.statusbar.NotificationListener +import com.android.systemui.statusbar.NotificationMediaManager +import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator +import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerShelfViewModel +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel +import com.android.systemui.statusbar.phone.DozeParameters +import com.android.systemui.statusbar.phone.KeyguardBypassController +import com.android.systemui.statusbar.phone.NotificationIconContainer +import com.android.systemui.statusbar.phone.ScreenOffAnimationController +import com.android.systemui.statusbar.window.StatusBarWindowController +import com.android.systemui.util.mockito.whenever +import com.android.wm.shell.bubbles.Bubbles +import java.util.Optional +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@RunWithLooper(setAsMainLooper = true) +class NotificationIconAreaControllerViewBinderWrapperImplTest : SysuiTestCase() { + @Mock private lateinit var notifListener: NotificationListener + @Mock private lateinit var statusBarStateController: StatusBarStateController + @Mock private lateinit var wakeUpCoordinator: NotificationWakeUpCoordinator + @Mock private lateinit var keyguardBypassController: KeyguardBypassController + @Mock private lateinit var notifMediaManager: NotificationMediaManager + @Mock private lateinit var dozeParams: DozeParameters + @Mock private lateinit var sectionStyleProvider: SectionStyleProvider + @Mock private lateinit var darkIconDispatcher: DarkIconDispatcher + @Mock private lateinit var statusBarWindowController: StatusBarWindowController + @Mock private lateinit var screenOffAnimController: ScreenOffAnimationController + @Mock private lateinit var bubbles: Bubbles + @Mock private lateinit var demoModeController: DemoModeController + @Mock private lateinit var aodIcons: NotificationIconContainer + @Mock private lateinit var featureFlags: FeatureFlags + + private val shelfViewModel = NotificationIconContainerShelfViewModel() + private val statusBarViewModel = NotificationIconContainerStatusBarViewModel() + private val aodViewModel = NotificationIconContainerAlwaysOnDisplayViewModel() + + private lateinit var underTest: NotificationIconAreaControllerViewBinderWrapperImpl + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + underTest = + NotificationIconAreaControllerViewBinderWrapperImpl( + mContext, + statusBarStateController, + wakeUpCoordinator, + keyguardBypassController, + notifMediaManager, + notifListener, + dozeParams, + sectionStyleProvider, + Optional.of(bubbles), + demoModeController, + darkIconDispatcher, + featureFlags, + statusBarWindowController, + screenOffAnimController, + shelfViewModel, + statusBarViewModel, + aodViewModel, + ) + } + + @Test + fun testNotificationIcons_settingHideIcons() { + underTest.settingsListener.onStatusBarIconsBehaviorChanged(true) + assertFalse(underTest.shouldShowLowPriorityIcons()) + } + + @Test + fun testNotificationIcons_settingShowIcons() { + underTest.settingsListener.onStatusBarIconsBehaviorChanged(false) + assertTrue(underTest.shouldShowLowPriorityIcons()) + } + + @Test + fun testAppearResetsTranslation() { + underTest.setupAodIcons(aodIcons) + whenever(dozeParams.shouldControlScreenOff()).thenReturn(false) + underTest.appearAodIcons() + verify(aodIcons).translationY = 0f + verify(aodIcons).alpha = 1.0f + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java index 8e1dcf0b1bb7..1b8cfd43e6b6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java @@ -48,7 +48,7 @@ import java.util.Optional; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper -public class NotificationIconAreaControllerTest extends SysuiTestCase { +public class LegacyNotificationIconAreaControllerImplTest extends SysuiTestCase { @Mock private NotificationListener mListener; @@ -70,7 +70,7 @@ public class NotificationIconAreaControllerTest extends SysuiTestCase { StatusBarWindowController mStatusBarWindowController; @Mock ScreenOffAnimationController mScreenOffAnimationController; - private NotificationIconAreaController mController; + private LegacyNotificationIconAreaControllerImpl mController; @Mock private Bubbles mBubbles; @Mock private DemoModeController mDemoModeController; @@ -82,7 +82,7 @@ public class NotificationIconAreaControllerTest extends SysuiTestCase { @Before public void setup() { MockitoAnnotations.initMocks(this); - mController = new NotificationIconAreaController( + mController = new LegacyNotificationIconAreaControllerImpl( mContext, mStatusBarStateController, mWakeUpCoordinator, |