Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4fc693ea authored by Steve Elliott's avatar Steve Elliott
Browse files

Move NIC binding to NotificationShelfViewBinder

Flag: NOTIFICATION_ICON_CONTAINER_REFACTOR
Bug: 290787599
Bug: 278765923
Test: atest SystemUITests
Change-Id: Idb8e7f10a4179961108385ae27444d16d465ddc0
parent ee1f0ac0
Loading
Loading
Loading
Loading
+70 −0
Original line number Diff line number Diff line
@@ -15,18 +15,26 @@
package com.android.systemui.common.ui

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.AttrRes
import androidx.annotation.ColorInt
import androidx.annotation.DimenRes
import androidx.annotation.LayoutRes
import com.android.settingslib.Utils
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.onDensityOrFontScaleChanged
import com.android.systemui.statusbar.policy.onThemeChanged
import com.android.systemui.util.kotlin.emitOnStart
import com.android.systemui.util.view.bindLatest
import javax.inject.Inject
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge

/** Configuration-aware-state-tracking utilities. */
class ConfigurationState
@@ -34,6 +42,7 @@ class ConfigurationState
constructor(
    private val configurationController: ConfigurationController,
    @Application private val context: Context,
    private val layoutInflater: LayoutInflater,
) {
    /**
     * Returns a [Flow] that emits a dimension pixel size that is kept in sync with the device
@@ -57,4 +66,65 @@ constructor(
            Utils.getColorAttrDefaultColor(context, id, defaultValue)
        }
    }

    /**
     * Returns a [Flow] that emits a [View] that is re-inflated as necessary to remain in sync with
     * the device configuration.
     *
     * @see LayoutInflater.inflate
     */
    @Suppress("UNCHECKED_CAST")
    fun <T : View> inflateLayout(
        @LayoutRes id: Int,
        root: ViewGroup?,
        attachToRoot: Boolean,
    ): Flow<T> {
        // TODO(b/305930747): This may lead to duplicate invocations if both flows emit, find a
        //  solution to only emit one event.
        return merge(
                configurationController.onThemeChanged,
                configurationController.onDensityOrFontScaleChanged,
            )
            .emitOnStart()
            .map { layoutInflater.inflate(id, root, attachToRoot) as T }
    }
}

/**
 * Perform an inflation right away, then re-inflate whenever the device configuration changes, and
 * call [onInflate] on the resulting view each time. Disposes of the [DisposableHandle] returned by
 * [onInflate] when done.
 *
 * This never completes unless cancelled, it just suspends and waits for updates.
 *
 * For parameters [resource], [root] and [attachToRoot], see [LayoutInflater.inflate].
 *
 * An example use-case of this is when a view needs to be re-inflated whenever a configuration
 * change occurs, which would require the ViewBinder to then re-bind the new view. For example, the
 * code in the parent view's binder would look like:
 * ```
 * parentView.repeatWhenAttached {
 *     configurationState
 *         .reinflateOnChange(
 *             R.layout.my_layout,
 *             parentView,
 *             attachToRoot = false,
 *             coroutineScope = lifecycleScope,
 *             configurationController.onThemeChanged,
 *         ) { view: ChildView ->
 *             ChildViewBinder.bind(view, childViewModel)
 *         }
 * }
 * ```
 *
 * In turn, the bind method (passed through [onInflate]) uses [repeatWhenAttached], which returns a
 * [DisposableHandle].
 */
suspend fun <T : View> ConfigurationState.reinflateAndBindLatest(
    @LayoutRes resource: Int,
    root: ViewGroup?,
    attachToRoot: Boolean,
    onInflate: (T) -> DisposableHandle?,
) {
    inflateLayout<T>(resource, root, attachToRoot).bindLatest(onInflate)
}
+1 −23
Original line number Diff line number Diff line
@@ -21,13 +21,10 @@ import android.view.View
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.flags.RefactorFlag
import com.android.systemui.statusbar.NotificationShelfController
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.notification.collection.ListEntry
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.shelf.ui.viewbinder.NotificationShelfViewBinderWrapperControllerImpl
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.NotificationIconAreaController
@@ -53,15 +50,10 @@ constructor(
    private val dozeParameters: DozeParameters,
    private val featureFlags: FeatureFlagsClassic,
    private val screenOffAnimationController: ScreenOffAnimationController,
    private val shelfIconViewStore: ShelfNotificationIconViewStore,
    private val shelfIconsViewModel: NotificationIconContainerShelfViewModel,
    private val aodIconViewStore: AlwaysOnDisplayNotificationIconViewStore,
    private val aodIconsViewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
) : NotificationIconAreaController {

    private val shelfRefactor = RefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR)

    private var shelfIcons: NotificationIconContainer? = null
    private var aodIcons: NotificationIconContainer? = null
    private var aodBindJob: DisposableHandle? = null

@@ -91,21 +83,7 @@ constructor(
    override fun setupShelf(notificationShelfController: NotificationShelfController) =
        NotificationShelfViewBinderWrapperControllerImpl.unsupported

    override fun setShelfIcons(icons: NotificationIconContainer) {
        if (shelfRefactor.isUnexpectedlyInLegacyMode()) {
            NotificationIconContainerViewBinder.bind(
                icons,
                shelfIconsViewModel,
                configuration,
                configurationController,
                dozeParameters,
                featureFlags,
                screenOffAnimationController,
                shelfIconViewStore,
            )
            shelfIcons = icons
        }
    }
    override fun setShelfIcons(icons: NotificationIconContainer) = unsupported

    override fun onDensityOrFontScaleChanged(context: Context) = unsupported

+28 −4
Original line number Diff line number Diff line
@@ -19,19 +19,26 @@ package com.android.systemui.statusbar.notification.shelf.ui.viewbinder
import android.view.View
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.LegacyNotificationShelfControllerImpl
import com.android.systemui.statusbar.NotificationShelf
import com.android.systemui.statusbar.NotificationShelfController
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.ShelfNotificationIconViewStore
import com.android.systemui.statusbar.notification.row.ui.viewbinder.ActivatableNotificationViewBinder
import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel
import com.android.systemui.statusbar.notification.stack.AmbientState
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.phone.DozeParameters
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.policy.ConfigurationController
import javax.inject.Inject
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.launch
@@ -75,14 +82,31 @@ object NotificationShelfViewBinder {
    fun bind(
        shelf: NotificationShelf,
        viewModel: NotificationShelfViewModel,
        configuration: ConfigurationState,
        configurationController: ConfigurationController,
        dozeParameters: DozeParameters,
        falsingManager: FalsingManager,
        featureFlags: FeatureFlags,
        featureFlags: FeatureFlagsClassic,
        notificationIconAreaController: NotificationIconAreaController,
        screenOffAnimationController: ScreenOffAnimationController,
        shelfIconViewStore: ShelfNotificationIconViewStore,
    ) {
        ActivatableNotificationViewBinder.bind(viewModel, shelf, falsingManager)
        shelf.apply {
            // TODO(278765923): Replace with eventual NotificationIconContainerViewBinder#bind()
            if (featureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
                NotificationIconContainerViewBinder.bind(
                    shelfIcons,
                    viewModel.icons,
                    configuration,
                    configurationController,
                    dozeParameters,
                    featureFlags,
                    screenOffAnimationController,
                    shelfIconViewStore,
                )
            } else {
                notificationIconAreaController.setShelfIcons(shelfIcons)
            }
            repeatWhenAttached {
                repeatOnLifecycle(Lifecycle.State.STARTED) {
                    launch {
+2 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.shelf.ui.viewmodel

import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.NotificationShelf
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerShelfViewModel
import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModel
import com.android.systemui.statusbar.notification.shelf.domain.interactor.NotificationShelfInteractor
import javax.inject.Inject
@@ -31,6 +32,7 @@ class NotificationShelfViewModel
constructor(
    private val interactor: NotificationShelfInteractor,
    activatableViewModel: ActivatableNotificationViewModel,
    val icons: NotificationIconContainerShelfViewModel,
) : ActivatableNotificationViewModel by activatableViewModel {
    /** Is the shelf allowed to be clickable when it has content? */
    val isClickable: Flow<Boolean>
+20 −3
Original line number Diff line number Diff line
@@ -59,6 +59,7 @@ import com.android.systemui.Gefingerpoken;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.common.ui.ConfigurationState;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
@@ -107,6 +108,7 @@ import com.android.systemui.statusbar.notification.collection.render.Notificatio
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
import com.android.systemui.statusbar.notification.dagger.SilentHeader;
import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor;
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.ShelfNotificationIconViewStore;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -117,10 +119,12 @@ 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.DozeParameters;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
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.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -212,6 +216,10 @@ public class NotificationStackScrollLayoutController {
    private final SecureSettings mSecureSettings;
    private final NotificationDismissibilityProvider mDismissibilityProvider;
    private final ActivityStarter mActivityStarter;
    private final ConfigurationState mConfigurationState;
    private final DozeParameters mDozeParameters;
    private final ScreenOffAnimationController mScreenOffAnimationController;
    private final ShelfNotificationIconViewStore mShelfIconViewStore;

    private View mLongPressedView;

@@ -674,7 +682,10 @@ public class NotificationStackScrollLayoutController {
            SecureSettings secureSettings,
            NotificationDismissibilityProvider dismissibilityProvider,
            ActivityStarter activityStarter,
            SplitShadeStateController splitShadeStateController) {
            SplitShadeStateController splitShadeStateController,
            ConfigurationState configurationState, DozeParameters dozeParameters,
            ScreenOffAnimationController screenOffAnimationController,
            ShelfNotificationIconViewStore shelfIconViewStore) {
        mView = view;
        mKeyguardTransitionRepo = keyguardTransitionRepo;
        mStackStateLogger = stackLogger;
@@ -724,6 +735,10 @@ public class NotificationStackScrollLayoutController {
        mSecureSettings = secureSettings;
        mDismissibilityProvider = dismissibilityProvider;
        mActivityStarter = activityStarter;
        mConfigurationState = configurationState;
        mDozeParameters = dozeParameters;
        mScreenOffAnimationController = screenOffAnimationController;
        mShelfIconViewStore = shelfIconViewStore;
        mView.passSplitShadeStateController(splitShadeStateController);
        updateResources();
        setUpView();
@@ -832,8 +847,10 @@ public class NotificationStackScrollLayoutController {

        mViewModel.ifPresent(
                vm -> NotificationListViewBinder
                        .bind(mView, vm, mFalsingManager, mFeatureFlags, mNotifIconAreaController,
                                mConfigurationController));
                        .bind(mView, vm, mConfigurationState, mConfigurationController,
                                mDozeParameters, mFalsingManager, mFeatureFlags,
                                mNotifIconAreaController, mScreenOffAnimationController,
                                mShelfIconViewStore));

        collectFlow(mView, mKeyguardTransitionRepo.getTransitions(),
                this::onKeyguardTransitionChanged);
Loading