ViewModel+Binder for NotificationStackScrollLayout
This change lays a foundation for more notifications code to be migrated to the Recommended Architecture. Right now, it primarily serves as a means to integrate the NotificationShelfViewBinder into the codebase without needed the NotificationShelfController interface any longer. As more and more child Views of the NSSL are migrated, the NSSLViewBinder will continue to grow. If we ever decide to migrate the NSSL itself, then code from the NSSLController will be moved into the NSSLViewBinder under a process similar to that implemented for the NotificationShelf. Bug: 271161129 Test: atest SystemUITests Change-Id: I8f605f8d7b4722d3c2e065c7b37cc4f23e6cc99a
This commit is contained in:
@@ -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<NotificationStackScrollLayoutController>,
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<NotificationListViewModel> 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<NotificationListViewModel> 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) {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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<NotificationShelfViewModel>,
|
||||
): Optional<NotificationListViewModel> =
|
||||
if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
|
||||
Optional.of(NotificationListViewModel(shelfViewModel.get()))
|
||||
} else {
|
||||
Optional.empty()
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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<StatusBarStateController.StateListener> mStateListenerArgumentCaptor;
|
||||
@@ -430,6 +435,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
|
||||
mKeyguardBypassController,
|
||||
mZenModeController,
|
||||
mNotificationLockscreenUserManager,
|
||||
Optional.<NotificationListViewModel>empty(),
|
||||
mMetricsLogger,
|
||||
mDumpManager,
|
||||
new FalsingCollectorFake(),
|
||||
@@ -453,6 +459,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
|
||||
mStackLogger,
|
||||
mLogger,
|
||||
mNotificationStackSizeCalculator,
|
||||
mIconAreaController,
|
||||
mFeatureFlags,
|
||||
mNotificationTargetsHelper,
|
||||
mSecureSettings,
|
||||
|
||||
Reference in New Issue
Block a user