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 c82318913ced0..99c6ddb8cfaa2 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 @@ -33,12 +33,9 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll import com.android.systemui.statusbar.phone.NotificationIconAreaController import com.android.systemui.statusbar.phone.NotificationIconContainer import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope -import com.android.systemui.util.kotlin.getValue -import dagger.Lazy import javax.inject.Inject import kotlinx.coroutines.awaitCancellation -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch /** * Controller class for [NotificationShelf]. This implementation serves as a temporary wrapper @@ -47,39 +44,12 @@ import kotlinx.coroutines.flow.onEach * removed, this class can go away and the ViewBinder can be used directly. */ @CentralSurfacesScope -class NotificationShelfViewBinderWrapperControllerImpl -@Inject -constructor( - private val shelf: NotificationShelf, - private val viewModel: NotificationShelfViewModel, - featureFlags: FeatureFlags, - private val falsingManager: FalsingManager, - hostControllerLazy: Lazy, - private val notificationIconAreaController: NotificationIconAreaController, -) : NotificationShelfController { - - private val hostController: NotificationStackScrollLayoutController by hostControllerLazy +class NotificationShelfViewBinderWrapperControllerImpl @Inject constructor() : + NotificationShelfController { override val view: NotificationShelf get() = unsupported - init { - shelf.apply { - setRefactorFlagEnabled(featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) - useRoundnessSourceTypes(featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES)) - setSensitiveRevealAnimEndabled(featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM)) - } - } - - fun init() { - NotificationShelfViewBinder.bind(viewModel, shelf, falsingManager) - hostController.setShelf(shelf) - hostController.setOnNotificationRemovedListener { child, _ -> - view.requestRoundnessResetFor(child) - } - notificationIconAreaController.setShelfIcons(shelf.shelfIcons) - } - override val intrinsicHeight: Int get() = unsupported @@ -99,21 +69,32 @@ constructor( get() = NotificationShelfController.throwIllegalFlagStateError(expected = true) } -/** Binds a [NotificationShelf] to its backend. */ +/** Binds a [NotificationShelf] to its [view model][NotificationShelfViewModel]. */ object NotificationShelfViewBinder { fun bind( - viewModel: NotificationShelfViewModel, shelf: NotificationShelf, + viewModel: NotificationShelfViewModel, falsingManager: FalsingManager, + featureFlags: FeatureFlags, + notificationIconAreaController: NotificationIconAreaController, ) { ActivatableNotificationViewBinder.bind(viewModel, shelf, falsingManager) - shelf.repeatWhenAttached { - repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.canModifyColorOfNotifications - .onEach(shelf::setCanModifyColorOfNotifications) - .launchIn(this) - viewModel.isClickable.onEach(shelf::setCanInteract).launchIn(this) - registerViewListenersWhileAttached(shelf, viewModel) + shelf.apply { + setRefactorFlagEnabled(true) + useRoundnessSourceTypes(featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES)) + setSensitiveRevealAnimEndabled(featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM)) + // TODO(278765923): Replace with eventual NotificationIconContainerViewBinder#bind() + notificationIconAreaController.setShelfIcons(shelfIcons) + repeatWhenAttached { + repeatOnLifecycle(Lifecycle.State.STARTED) { + launch { + viewModel.canModifyColorOfNotifications.collect( + ::setCanModifyColorOfNotifications + ) + } + launch { viewModel.isClickable.collect(::setCanInteract) } + registerViewListenersWhileAttached(shelf, viewModel) + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 555d5027c7b64..587783d87ba5a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -2839,6 +2839,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable * @param listener callback for notification removed */ public void setOnNotificationRemovedListener(OnNotificationRemovedListener listener) { + NotificationShelfController.assertRefactorFlagDisabled(mAmbientState.getFeatureFlags()); mOnNotificationRemovedListener = listener; } @@ -2852,10 +2853,14 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable if (!mChildTransferInProgress) { onViewRemovedInternal(expandableView, this); } - if (mOnNotificationRemovedListener != null) { - mOnNotificationRemovedListener.onNotificationRemoved( - expandableView, - mChildTransferInProgress); + if (mAmbientState.getFeatureFlags().isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) { + mShelf.requestRoundnessResetFor(expandableView); + } else { + if (mOnNotificationRemovedListener != null) { + mOnNotificationRemovedListener.onNotificationRemoved( + expandableView, + mChildTransferInProgress); + } } } 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 9979cc4fa9d5a..b69ce38613422 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 @@ -105,11 +105,14 @@ import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.row.NotificationGuts; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationSnooze; +import com.android.systemui.statusbar.notification.stack.ui.viewbinder.NotificationListViewBinder; +import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel; import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.HeadsUpTouchHelper; import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.statusbar.phone.NotificationIconAreaController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -122,16 +125,17 @@ import com.android.systemui.tuner.TunerService; import com.android.systemui.util.Compile; import com.android.systemui.util.settings.SecureSettings; +import kotlin.Unit; + import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.function.BiConsumer; import java.util.function.Consumer; import javax.inject.Inject; import javax.inject.Named; -import kotlin.Unit; - /** * Controller for {@link NotificationStackScrollLayout}. */ @@ -151,6 +155,8 @@ public class NotificationStackScrollLayoutController { private final ConfigurationController mConfigurationController; private final ZenModeController mZenModeController; private final MetricsLogger mMetricsLogger; + private final Optional mViewModel; + private final DumpManager mDumpManager; private final FalsingCollector mFalsingCollector; private final FalsingManager mFalsingManager; @@ -175,6 +181,8 @@ public class NotificationStackScrollLayoutController { private final NotificationStackSizeCalculator mNotificationStackSizeCalculator; private final StackStateLogger mStackStateLogger; private final NotificationStackScrollLogger mLogger; + private final NotificationIconAreaController mNotifIconAreaController; + private final GroupExpansionManager mGroupExpansionManager; private final NotifPipelineFlags mNotifPipelineFlags; private final SeenNotificationsProvider mSeenNotificationsProvider; @@ -642,6 +650,7 @@ public class NotificationStackScrollLayoutController { KeyguardBypassController keyguardBypassController, ZenModeController zenModeController, NotificationLockscreenUserManager lockscreenUserManager, + Optional nsslViewModel, MetricsLogger metricsLogger, DumpManager dumpManager, FalsingCollector falsingCollector, @@ -665,6 +674,7 @@ public class NotificationStackScrollLayoutController { StackStateLogger stackLogger, NotificationStackScrollLogger logger, NotificationStackSizeCalculator notificationStackSizeCalculator, + NotificationIconAreaController notifIconAreaController, FeatureFlags featureFlags, NotificationTargetsHelper notificationTargetsHelper, SecureSettings secureSettings, @@ -686,6 +696,7 @@ public class NotificationStackScrollLayoutController { mKeyguardBypassController = keyguardBypassController; mZenModeController = zenModeController; mLockscreenUserManager = lockscreenUserManager; + mViewModel = nsslViewModel; mMetricsLogger = metricsLogger; mDumpManager = dumpManager; mLockscreenShadeTransitionController = lockscreenShadeTransitionController; @@ -707,6 +718,7 @@ public class NotificationStackScrollLayoutController { mVisibilityLocationProviderDelegator = visibilityLocationProviderDelegator; mSeenNotificationsProvider = seenNotificationsProvider; mShadeController = shadeController; + mNotifIconAreaController = notifIconAreaController; mFeatureFlags = featureFlags; mUseRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES); mNotificationTargetsHelper = notificationTargetsHelper; @@ -820,6 +832,10 @@ public class NotificationStackScrollLayoutController { mGroupExpansionManager.registerGroupExpansionChangeListener( (changedRow, expanded) -> mView.onGroupExpandChanged(changedRow, expanded)); + + mViewModel.ifPresent( + vm -> NotificationListViewBinder + .bind(mView, vm, mFalsingManager, mFeatureFlags, mNotifIconAreaController)); } private boolean isInVisibleLocation(NotificationEntry entry) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt new file mode 100644 index 0000000000000..45ae4e0afc3ac --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt @@ -0,0 +1,51 @@ +/* + * 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.stack.ui.viewbinder + +import android.view.LayoutInflater +import com.android.systemui.R +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.plugins.FalsingManager +import com.android.systemui.statusbar.NotificationShelf +import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinder +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout +import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel +import com.android.systemui.statusbar.phone.NotificationIconAreaController + +/** Binds a [NotificationStackScrollLayout] to its [view model][NotificationListViewModel]. */ +object NotificationListViewBinder { + @JvmStatic + fun bind( + view: NotificationStackScrollLayout, + viewModel: NotificationListViewModel, + falsingManager: FalsingManager, + featureFlags: FeatureFlags, + iconAreaController: NotificationIconAreaController, + ) { + val shelf = + LayoutInflater.from(view.context) + .inflate(R.layout.status_bar_notification_shelf, view, false) as NotificationShelf + NotificationShelfViewBinder.bind( + shelf, + viewModel.shelf, + falsingManager, + featureFlags, + iconAreaController + ) + view.setShelf(shelf) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt new file mode 100644 index 0000000000000..aab1c2b877e8b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt @@ -0,0 +1,45 @@ +/* + * 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.stack.ui.viewmodel + +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel +import dagger.Module +import dagger.Provides +import java.util.Optional +import javax.inject.Provider + +/** ViewModel for the list of notifications. */ +class NotificationListViewModel( + val shelf: NotificationShelfViewModel, +) + +@Module +object NotificationListViewModelModule { + @JvmStatic + @Provides + fun maybeProvideViewModel( + featureFlags: FeatureFlags, + shelfViewModel: Provider, + ): Optional = + if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) { + Optional.of(NotificationListViewModel(shelfViewModel.get())) + } else { + Optional.empty() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java index 95d36b082b57b..ef86162aecf53 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java @@ -55,6 +55,7 @@ import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfC import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModelModule; import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinderWrapperControllerImpl; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; +import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModelModule; import com.android.systemui.statusbar.phone.KeyguardBottomAreaView; import com.android.systemui.statusbar.phone.NotificationIconAreaController; import com.android.systemui.statusbar.phone.StatusBarBoundsProvider; @@ -87,7 +88,10 @@ import javax.inject.Named; import javax.inject.Provider; @Module(subcomponents = StatusBarFragmentComponent.class, - includes = { ActivatableNotificationViewModelModule.class }) + includes = { + ActivatableNotificationViewModelModule.class, + NotificationListViewModelModule.class, + }) public abstract class StatusBarViewModule { public static final String SHADE_HEADER = "large_screen_shade_header"; @@ -117,9 +121,7 @@ public abstract class StatusBarViewModule { NotificationShelfComponent.Builder notificationShelfComponentBuilder, NotificationShelf notificationShelf) { if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) { - NotificationShelfViewBinderWrapperControllerImpl impl = newImpl.get(); - impl.init(); - return impl; + return newImpl.get(); } else { NotificationShelfComponent component = notificationShelfComponentBuilder .notificationShelf(notificationShelf) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java index 0ae012cd5e908..fbbb921f96a0a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java @@ -72,9 +72,11 @@ import com.android.systemui.statusbar.notification.collection.render.SectionHead import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController.NotificationPanelEvent; +import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel; import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.statusbar.phone.NotificationIconAreaController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; @@ -92,6 +94,8 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Optional; + /** * Tests for {@link NotificationStackScrollLayoutController}. */ @@ -138,6 +142,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { @Mock private FeatureFlags mFeatureFlags; @Mock private NotificationTargetsHelper mNotificationTargetsHelper; @Mock private SecureSettings mSecureSettings; + @Mock private NotificationIconAreaController mIconAreaController; @Captor private ArgumentCaptor mStateListenerArgumentCaptor; @@ -430,6 +435,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { mKeyguardBypassController, mZenModeController, mNotificationLockscreenUserManager, + Optional.empty(), mMetricsLogger, mDumpManager, new FalsingCollectorFake(), @@ -453,6 +459,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { mStackLogger, mLogger, mNotificationStackSizeCalculator, + mIconAreaController, mFeatureFlags, mNotificationTargetsHelper, mSecureSettings,