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

Commit 26b337e4 authored by Steve Elliott's avatar Steve Elliott
Browse files

Fix test failures when NICRefactor flag is enabled

Failures are caused by running view-binder logic inside of unrelated
unit tests; these tests do not actually verify anything pertaining to
the NotificationIconContainer, so this CL restructures the code so that
we can mock out the view-binder.

Actual view-binder logic is verified via existing NIC screenshot tests.

Flag: ACONFIG com.android.systemui.notifications_icon_container_refactor DEVELOPMENT
Test: atest SystemUITests
Bug: 278765923
Change-Id: I1af34b9e96616c5a923d87fbf25625fd36024544
parent 0dd91528
Loading
Loading
Loading
Loading
+5 −56
Original line number Diff line number Diff line
@@ -39,7 +39,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;

import com.android.systemui.Dumpable;
import com.android.systemui.common.ui.ConfigurationState;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
@@ -48,9 +47,7 @@ import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl;
import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder;
import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.core.LogLevel;
import com.android.systemui.log.dagger.KeyguardClockLog;
@@ -62,17 +59,11 @@ import com.android.systemui.shared.regionsampling.RegionSampler;
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.AlwaysOnDisplayNotificationIconViewStore;
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder;
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker;
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel;
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerAlwaysOnDisplayViewBinder;
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
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.ui.SystemBarUtilsState;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.settings.SecureSettings;
@@ -102,14 +93,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
    private final DumpManager mDumpManager;
    private final ClockEventController mClockEventController;
    private final LogBuffer mLogBuffer;
    private final NotificationIconContainerAlwaysOnDisplayViewModel mAodIconsViewModel;
    private final KeyguardRootViewModel mKeyguardRootViewModel;
    private final ConfigurationState mConfigurationState;
    private final SystemBarUtilsState mSystemBarUtilsState;
    private final DozeParameters mDozeParameters;
    private final ScreenOffAnimationController mScreenOffAnimationController;
    private final AlwaysOnDisplayNotificationIconViewStore mAodIconViewStore;
    private final StatusBarIconViewBindingFailureTracker mIconViewBindingFailureTracker;
    private final NotificationIconContainerAlwaysOnDisplayViewBinder mNicViewBinder;
    private FrameLayout mSmallClockFrame; // top aligned clock
    private FrameLayout mLargeClockFrame; // centered clock

@@ -183,9 +167,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
            KeyguardSliceViewController keyguardSliceViewController,
            NotificationIconAreaController notificationIconAreaController,
            LockscreenSmartspaceController smartspaceController,
            SystemBarUtilsState systemBarUtilsState,
            ScreenOffAnimationController screenOffAnimationController,
            StatusBarIconViewBindingFailureTracker iconViewBindingFailureTracker,
            NotificationIconContainerAlwaysOnDisplayViewBinder nicViewBinder,
            KeyguardUnlockAnimationController keyguardUnlockAnimationController,
            SecureSettings secureSettings,
            @Main DelayableExecutor uiExecutor,
@@ -193,11 +175,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
            DumpManager dumpManager,
            ClockEventController clockEventController,
            @KeyguardClockLog LogBuffer logBuffer,
            NotificationIconContainerAlwaysOnDisplayViewModel aodIconsViewModel,
            KeyguardRootViewModel keyguardRootViewModel,
            ConfigurationState configurationState,
            DozeParameters dozeParameters,
            AlwaysOnDisplayNotificationIconViewStore aodIconViewStore,
            KeyguardInteractor keyguardInteractor,
            KeyguardClockInteractor keyguardClockInteractor,
            FeatureFlagsClassic featureFlags,
@@ -208,9 +185,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
        mKeyguardSliceViewController = keyguardSliceViewController;
        mNotificationIconAreaController = notificationIconAreaController;
        mSmartspaceController = smartspaceController;
        mSystemBarUtilsState = systemBarUtilsState;
        mScreenOffAnimationController = screenOffAnimationController;
        mIconViewBindingFailureTracker = iconViewBindingFailureTracker;
        mNicViewBinder = nicViewBinder;
        mSecureSettings = secureSettings;
        mUiExecutor = uiExecutor;
        mBgExecutor = bgExecutor;
@@ -218,11 +193,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
        mDumpManager = dumpManager;
        mClockEventController = clockEventController;
        mLogBuffer = logBuffer;
        mAodIconsViewModel = aodIconsViewModel;
        mKeyguardRootViewModel = keyguardRootViewModel;
        mConfigurationState = configurationState;
        mDozeParameters = dozeParameters;
        mAodIconViewStore = aodIconViewStore;
        mView.setLogBuffer(mLogBuffer);
        mFeatureFlags = featureFlags;
        mKeyguardInteractor = keyguardInteractor;
@@ -619,28 +589,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
                    mAodIconsBindHandle.dispose();
                }
                if (nic != null) {
                    final DisposableHandle viewHandle =
                            NotificationIconContainerViewBinder.bindWhileAttached(
                                    nic,
                                    mAodIconsViewModel,
                                    mConfigurationState,
                                    mSystemBarUtilsState,
                                    mIconViewBindingFailureTracker,
                                    mAodIconViewStore);
                    final DisposableHandle visHandle = KeyguardRootViewBinder.bindAodIconVisibility(
                            nic,
                            mKeyguardRootViewModel.isNotifIconContainerVisible(),
                            mConfigurationState,
                            mFeatureFlags,
                            mScreenOffAnimationController);
                    if (visHandle == null) {
                        mAodIconsBindHandle = viewHandle;
                    } else {
                        mAodIconsBindHandle = () -> {
                            viewHandle.dispose();
                            visHandle.dispose();
                        };
                    }
                    mAodIconsBindHandle = mNicViewBinder.bindWhileAttached(nic);
                    mAodIconContainer = nic;
                }
            } else {
+13 −22
Original line number Diff line number Diff line
@@ -29,7 +29,6 @@ import android.view.ViewGroup.OnHierarchyChangeListener
import android.view.ViewPropertyAnimator
import android.view.WindowInsets
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.animation.Interpolators
import com.android.internal.jank.InteractionJankMonitor
@@ -67,6 +66,7 @@ import com.android.systemui.util.ui.value
import javax.inject.Provider
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@@ -205,7 +205,6 @@ object KeyguardRootViewBinder {
                                    childViews[aodNotificationIconContainerId]
                                        ?.setAodNotifIconContainerIsVisible(
                                            isVisible,
                                            featureFlags,
                                            iconsAppearTranslationPx.value,
                                            screenOffAnimationController,
                                        )
@@ -359,37 +358,29 @@ object KeyguardRootViewBinder {
        }
    }

    @JvmStatic
    fun bindAodIconVisibility(
    suspend fun bindAodNotifIconVisibility(
        view: View,
        isVisible: Flow<AnimatedValue<Boolean>>,
        configuration: ConfigurationState,
        featureFlags: FeatureFlagsClassic,
        screenOffAnimationController: ScreenOffAnimationController,
    ): DisposableHandle? {
    ) {
        KeyguardShadeMigrationNssl.assertInLegacyMode()
        if (NotificationIconContainerRefactor.isUnexpectedlyInLegacyMode()) return null
        return view.repeatWhenAttached {
            lifecycleScope.launch {
        if (NotificationIconContainerRefactor.isUnexpectedlyInLegacyMode()) return
        coroutineScope {
            val iconAppearTranslationPx =
                    configuration
                        .getDimensionPixelSize(R.dimen.shelf_appear_translation)
                        .stateIn(this)
                configuration.getDimensionPixelSize(R.dimen.shelf_appear_translation).stateIn(this)
            isVisible.collect { isVisible ->
                view.setAodNotifIconContainerIsVisible(
                        isVisible,
                        featureFlags,
                        iconAppearTranslationPx.value,
                        screenOffAnimationController,
                    isVisible = isVisible,
                    iconsAppearTranslationPx = iconAppearTranslationPx.value,
                    screenOffAnimationController = screenOffAnimationController,
                )
            }
        }
    }
    }

    private fun View.setAodNotifIconContainerIsVisible(
        isVisible: AnimatedValue<Boolean>,
        featureFlags: FeatureFlagsClassic,
        iconsAppearTranslationPx: Int,
        screenOffAnimationController: ScreenOffAnimationController,
    ) {
+76 −0
Original line number Diff line number Diff line
/*
 * 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.lifecycleScope
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
import com.android.systemui.statusbar.phone.NotificationIconContainer
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
import com.android.systemui.statusbar.ui.SystemBarUtilsState
import javax.inject.Inject
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.launch

/** Binds a [NotificationIconContainer] to a [NotificationIconContainerAlwaysOnDisplayViewModel]. */
class NotificationIconContainerAlwaysOnDisplayViewBinder
@Inject
constructor(
    private val viewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
    private val keyguardRootViewModel: KeyguardRootViewModel,
    private val configuration: ConfigurationState,
    private val failureTracker: StatusBarIconViewBindingFailureTracker,
    private val screenOffAnimationController: ScreenOffAnimationController,
    private val systemBarUtilsState: SystemBarUtilsState,
    private val viewStore: AlwaysOnDisplayNotificationIconViewStore,
) {
    fun bindWhileAttached(view: NotificationIconContainer): DisposableHandle {
        return view.repeatWhenAttached {
            lifecycleScope.launch {
                launch {
                    NotificationIconContainerViewBinder.bind(
                        view = view,
                        viewModel = viewModel,
                        configuration = configuration,
                        systemBarUtilsState = systemBarUtilsState,
                        failureTracker = failureTracker,
                        viewStore = viewStore,
                    )
                }
                launch {
                    KeyguardRootViewBinder.bindAodNotifIconVisibility(
                        view = view,
                        isVisible = keyguardRootViewModel.isNotifIconContainerVisible,
                        configuration = configuration,
                        screenOffAnimationController = screenOffAnimationController,
                    )
                }
            }
        }
    }
}

/** [IconViewStore] for the always-on display. */
class AlwaysOnDisplayNotificationIconViewStore
@Inject
constructor(notifCollection: NotifCollection) :
    IconViewStore by (notifCollection.iconViewStoreBy { it.aodIcon })
+51 −0
Original line number Diff line number Diff line
/*
 * 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 com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.bindIcons
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerShelfViewModel
import com.android.systemui.statusbar.phone.NotificationIconContainer
import com.android.systemui.statusbar.ui.SystemBarUtilsState
import javax.inject.Inject

/** Binds a [NotificationIconContainer] to a [NotificationIconContainerShelfViewModel]. */
class NotificationIconContainerShelfViewBinder
@Inject
constructor(
    private val viewModel: NotificationIconContainerShelfViewModel,
    private val configuration: ConfigurationState,
    private val systemBarUtilsState: SystemBarUtilsState,
    private val failureTracker: StatusBarIconViewBindingFailureTracker,
    private val viewStore: ShelfNotificationIconViewStore,
) {
    suspend fun bind(view: NotificationIconContainer) {
        viewModel.icons.bindIcons(
            view,
            configuration,
            systemBarUtilsState,
            notifyBindingFailures = { failureTracker.shelfFailures = it },
            viewStore,
        )
    }
}

/** [IconViewStore] for the [com.android.systemui.statusbar.NotificationShelf] */
class ShelfNotificationIconViewStore @Inject constructor(notifCollection: NotifCollection) :
    IconViewStore by (notifCollection.iconViewStoreBy { it.shelfIcon })
+59 −0
Original line number Diff line number Diff line
/*
 * 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.lifecycleScope
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel
import com.android.systemui.statusbar.phone.NotificationIconContainer
import com.android.systemui.statusbar.ui.SystemBarUtilsState
import javax.inject.Inject
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.launch

/** Binds a [NotificationIconContainer] to a [NotificationIconContainerStatusBarViewModel]. */
class NotificationIconContainerStatusBarViewBinder
@Inject
constructor(
    private val viewModel: NotificationIconContainerStatusBarViewModel,
    private val configuration: ConfigurationState,
    private val systemBarUtilsState: SystemBarUtilsState,
    private val failureTracker: StatusBarIconViewBindingFailureTracker,
    private val viewStore: StatusBarNotificationIconViewStore,
) {
    fun bindWhileAttached(view: NotificationIconContainer): DisposableHandle {
        return view.repeatWhenAttached {
            lifecycleScope.launch {
                NotificationIconContainerViewBinder.bind(
                    view = view,
                    viewModel = viewModel,
                    configuration = configuration,
                    systemBarUtilsState = systemBarUtilsState,
                    failureTracker = failureTracker,
                    viewStore = viewStore,
                )
            }
        }
    }
}

/** [IconViewStore] for the status bar. */
class StatusBarNotificationIconViewStore @Inject constructor(notifCollection: NotifCollection) :
    IconViewStore by (notifCollection.iconViewStoreBy { it.statusBarIcon })
Loading