[CS] Create NotifExpansionRepo to track expansion animation.

To remove @CentralSurfacesScope, we need to remove some dependency
cycles. Some of the cycles come from
NotificationLaunchAnimatorController directly depending on
NotificationShadeWindowViewController. This CL adds a new
NotificationExpansionRepo that the two classes can each depend on,
instead of depending on each other.

Bug: 277762009
Test: verified notif expansion animation still plays correctly
Test: verified via logging that the notif shade classes still get the
expansion animation boolean correctly set
Test: atest NotificationExpansionRepositoryTest
NotificationShadeWindowViewControllerTest
NotificationLaunchAnimatorControllerTest

Change-Id: If4292721dd629731c1153bf1ea6b2697983d4924
This commit is contained in:
Caitlin Shkuratov
2023-07-24 14:43:14 +00:00
parent 076a75f35c
commit ea22d192be
10 changed files with 170 additions and 15 deletions

View File

@@ -61,6 +61,7 @@ import com.android.systemui.statusbar.NotificationInsetsController;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
@@ -157,6 +158,7 @@ public class NotificationShadeWindowViewController {
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
KeyguardTransitionInteractor keyguardTransitionInteractor,
PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel,
NotificationExpansionRepository notificationExpansionRepository,
FeatureFlags featureFlags,
SystemClock clock,
BouncerMessageInteractor bouncerMessageInteractor,
@@ -201,6 +203,10 @@ public class NotificationShadeWindowViewController {
collectFlow(mView, keyguardTransitionInteractor.getLockscreenToDreamingTransition(),
mLockscreenToDreamingTransition);
collectFlow(
mView,
notificationExpansionRepository.isExpandAnimationRunning(),
this::setExpandAnimationRunning);
mClock = clock;
if (featureFlags.isEnabled(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION)) {
@@ -518,7 +524,7 @@ public class NotificationShadeWindowViewController {
pw.println(mTouchActive);
}
public void setExpandAnimationRunning(boolean running) {
private void setExpandAnimationRunning(boolean running) {
if (mExpandAnimationRunning != running) {
mExpandAnimationRunning = running;
mNotificationShadeWindowController.setLaunchingActivity(mExpandAnimationRunning);

View File

@@ -1,10 +1,26 @@
/*
* Copyright (C) 2021 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
import android.view.ViewGroup
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.animation.LaunchAnimator
import com.android.systemui.shade.NotificationShadeWindowViewController
import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
@@ -17,7 +33,7 @@ import kotlin.math.max
/** A provider of [NotificationLaunchAnimatorController]. */
@CentralSurfacesComponent.CentralSurfacesScope
class NotificationLaunchAnimatorControllerProvider @Inject constructor(
private val notificationShadeWindowViewController: NotificationShadeWindowViewController,
private val notificationExpansionRepository: NotificationExpansionRepository,
private val notificationListContainer: NotificationListContainer,
private val headsUpManager: HeadsUpManagerPhone,
private val jankMonitor: InteractionJankMonitor
@@ -28,7 +44,7 @@ class NotificationLaunchAnimatorControllerProvider @Inject constructor(
onFinishAnimationCallback: Runnable? = null
): NotificationLaunchAnimatorController {
return NotificationLaunchAnimatorController(
notificationShadeWindowViewController,
notificationExpansionRepository,
notificationListContainer,
headsUpManager,
notification,
@@ -44,7 +60,7 @@ class NotificationLaunchAnimatorControllerProvider @Inject constructor(
* notification expanding into an opening window.
*/
class NotificationLaunchAnimatorController(
private val notificationShadeWindowViewController: NotificationShadeWindowViewController,
private val notificationExpansionRepository: NotificationExpansionRepository,
private val notificationListContainer: NotificationListContainer,
private val headsUpManager: HeadsUpManagerPhone,
private val notification: ExpandableNotificationRow,
@@ -119,7 +135,7 @@ class NotificationLaunchAnimatorController(
}
override fun onIntentStarted(willAnimate: Boolean) {
notificationShadeWindowViewController.setExpandAnimationRunning(willAnimate)
notificationExpansionRepository.setIsExpandAnimationRunning(willAnimate)
notificationEntry.isExpandAnimationRunning = willAnimate
if (!willAnimate) {
@@ -140,7 +156,7 @@ class NotificationLaunchAnimatorController(
override fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean?) {
// TODO(b/184121838): Should we call InteractionJankMonitor.cancel if the animation started
// here?
notificationShadeWindowViewController.setExpandAnimationRunning(false)
notificationExpansionRepository.setIsExpandAnimationRunning(false)
notificationEntry.isExpandAnimationRunning = false
removeHun(animate = true)
onFinishAnimationCallback?.run()
@@ -158,7 +174,7 @@ class NotificationLaunchAnimatorController(
jankMonitor.end(InteractionJankMonitor.CUJ_NOTIFICATION_APP_START)
notification.isExpandAnimationRunning = false
notificationShadeWindowViewController.setExpandAnimationRunning(false)
notificationExpansionRepository.setIsExpandAnimationRunning(false)
notificationEntry.isExpandAnimationRunning = false
notificationListContainer.setExpandingNotification(null)
applyParams(null)

View File

@@ -0,0 +1,42 @@
/*
* 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.data.repository
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
/** A repository tracking the status of notification expansion animations. */
@SysUISingleton
class NotificationExpansionRepository @Inject constructor() {
private val _isExpandAnimationRunning = MutableStateFlow(false)
/**
* Emits true if an animation that expands a notification object into an opening window is
* running and false otherwise.
*
* See [com.android.systemui.statusbar.notification.NotificationLaunchAnimatorController].
*/
val isExpandAnimationRunning: Flow<Boolean> = _isExpandAnimationRunning.asStateFlow()
/** Sets whether the notification expansion animation is currently running. */
fun setIsExpandAnimationRunning(running: Boolean) {
_isExpandAnimationRunning.value = running
}
}

View File

@@ -220,6 +220,7 @@ import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
@@ -312,6 +313,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private CentralSurfacesCommandQueueCallbacks mCommandQueueCallbacks;
private float mTransitionToFullShadeProgress = 0f;
private final NotificationListContainer mNotifListContainer;
private final NotificationExpansionRepository mNotificationExpansionRepository;
private boolean mIsShortcutListSearchEnabled;
private final KeyguardStateController.Callback mKeyguardStateControllerCallback =
@@ -725,6 +727,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
NotificationShelfController notificationShelfController,
NotificationStackScrollLayoutController notificationStackScrollLayoutController,
NotificationPresenter notificationPresenter,
NotificationExpansionRepository notificationExpansionRepository,
DozeParameters dozeParameters,
ScrimController scrimController,
Lazy<LockscreenWallpaper> lockscreenWallpaperLazy,
@@ -832,6 +835,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mStackScroller = mStackScrollerController.getView();
mNotifListContainer = mStackScrollerController.getNotificationListContainer();
mPresenter = notificationPresenter;
mNotificationExpansionRepository = notificationExpansionRepository;
mDozeServiceHost = dozeServiceHost;
mPowerManager = powerManager;
mDozeParameters = dozeParameters;
@@ -1546,7 +1550,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mActivityLaunchAnimator.setCallback(mActivityLaunchAnimatorCallback);
mActivityLaunchAnimator.addListener(mActivityLaunchAnimatorListener);
mNotificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider(
getNotificationShadeWindowViewController(),
mNotificationExpansionRepository,
mNotifListContainer,
mHeadsUpManager,
mJankMonitor);

View File

@@ -50,6 +50,7 @@ import com.android.systemui.statusbar.NotificationInsetsController
import com.android.systemui.statusbar.NotificationShadeDepthController
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository
import com.android.systemui.statusbar.notification.stack.AmbientState
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.phone.CentralSurfaces
@@ -117,6 +118,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
@Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
@Mock
lateinit var primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel
private val notificationExpansionRepository = NotificationExpansionRepository()
private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
private lateinit var interactionEventHandler: InteractionEventHandler
@@ -175,6 +177,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
mock(KeyguardMessageAreaController.Factory::class.java),
keyguardTransitionInteractor,
primaryBouncerToGoneTransitionViewModel,
notificationExpansionRepository,
featureFlags,
FakeSystemClock(),
BouncerMessageInteractor(

View File

@@ -50,6 +50,7 @@ import com.android.systemui.statusbar.NotificationInsetsController
import com.android.systemui.statusbar.NotificationShadeDepthController
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository
import com.android.systemui.statusbar.notification.stack.AmbientState
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
@@ -185,6 +186,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
Mockito.mock(KeyguardMessageAreaController.Factory::class.java),
keyguardTransitionInteractor,
primaryBouncerToGoneTransitionViewModel,
NotificationExpansionRepository(),
featureFlags,
FakeSystemClock(),
BouncerMessageInteractor(

View File

@@ -6,7 +6,8 @@ import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.SysuiTestCase
import com.android.systemui.shade.NotificationShadeWindowViewController
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.NotificationTestHelper
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
@@ -14,6 +15,7 @@ import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
import com.android.systemui.statusbar.policy.HeadsUpUtil
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import kotlinx.coroutines.test.TestScope
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -27,7 +29,6 @@ import org.mockito.junit.MockitoJUnit
@RunWith(AndroidTestingRunner::class)
@RunWithLooper
class NotificationLaunchAnimatorControllerTest : SysuiTestCase() {
@Mock lateinit var notificationShadeWindowViewController: NotificationShadeWindowViewController
@Mock lateinit var notificationListContainer: NotificationListContainer
@Mock lateinit var headsUpManager: HeadsUpManagerPhone
@Mock lateinit var jankMonitor: InteractionJankMonitor
@@ -36,6 +37,9 @@ class NotificationLaunchAnimatorControllerTest : SysuiTestCase() {
private lateinit var notificationTestHelper: NotificationTestHelper
private lateinit var notification: ExpandableNotificationRow
private lateinit var controller: NotificationLaunchAnimatorController
private val notificationExpansionRepository = NotificationExpansionRepository()
private val testScope = TestScope()
private val notificationKey: String
get() = notification.entry.sbn.key
@@ -49,7 +53,7 @@ class NotificationLaunchAnimatorControllerTest : SysuiTestCase() {
NotificationTestHelper(mContext, mDependency, TestableLooper.get(this))
notification = notificationTestHelper.createRow()
controller = NotificationLaunchAnimatorController(
notificationShadeWindowViewController,
notificationExpansionRepository,
notificationListContainer,
headsUpManager,
notification,
@@ -69,6 +73,11 @@ class NotificationLaunchAnimatorControllerTest : SysuiTestCase() {
assertTrue(HeadsUpUtil.isClickedHeadsUpNotification(notification))
assertFalse(notification.entry.isExpandAnimationRunning)
val isExpandAnimationRunning by testScope.collectLastValue(
notificationExpansionRepository.isExpandAnimationRunning
)
assertFalse(isExpandAnimationRunning!!)
verify(headsUpManager).removeNotification(
notificationKey, true /* releaseImmediately */, true /* animate */)
verify(onFinishAnimationCallback).run()
@@ -81,6 +90,11 @@ class NotificationLaunchAnimatorControllerTest : SysuiTestCase() {
assertTrue(HeadsUpUtil.isClickedHeadsUpNotification(notification))
assertFalse(notification.entry.isExpandAnimationRunning)
val isExpandAnimationRunning by testScope.collectLastValue(
notificationExpansionRepository.isExpandAnimationRunning
)
assertFalse(isExpandAnimationRunning!!)
verify(headsUpManager).removeNotification(
notificationKey, true /* releaseImmediately */, true /* animate */)
verify(onFinishAnimationCallback).run()
@@ -93,6 +107,11 @@ class NotificationLaunchAnimatorControllerTest : SysuiTestCase() {
assertFalse(HeadsUpUtil.isClickedHeadsUpNotification(notification))
assertFalse(notification.entry.isExpandAnimationRunning)
val isExpandAnimationRunning by testScope.collectLastValue(
notificationExpansionRepository.isExpandAnimationRunning
)
assertFalse(isExpandAnimationRunning!!)
verify(headsUpManager).removeNotification(
notificationKey, true /* releaseImmediately */, false /* animate */)
verify(onFinishAnimationCallback).run()
@@ -103,5 +122,9 @@ class NotificationLaunchAnimatorControllerTest : SysuiTestCase() {
controller.onIntentStarted(willAnimate = true)
assertTrue(notification.entry.isExpandAnimationRunning)
val isExpandAnimationRunning by testScope.collectLastValue(
notificationExpansionRepository.isExpandAnimationRunning
)
assertTrue(isExpandAnimationRunning!!)
}
}

View File

@@ -0,0 +1,57 @@
/*
* 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.data.repository
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Test
@SmallTest
class NotificationExpansionRepositoryTest : SysuiTestCase() {
private val underTest = NotificationExpansionRepository()
@Test
fun setIsExpandAnimationRunning_startsAsFalse() = runTest {
val latest by collectLastValue(underTest.isExpandAnimationRunning)
assertThat(latest).isFalse()
}
@Test
fun setIsExpandAnimationRunning_false_emitsTrue() = runTest {
val latest by collectLastValue(underTest.isExpandAnimationRunning)
underTest.setIsExpandAnimationRunning(true)
assertThat(latest).isTrue()
}
@Test
fun setIsExpandAnimationRunning_false_emitsFalse() = runTest {
val latest by collectLastValue(underTest.isExpandAnimationRunning)
underTest.setIsExpandAnimationRunning(true)
// WHEN the animation is no longer running
underTest.setIsExpandAnimationRunning(false)
// THEN the flow emits false
assertThat(latest).isFalse()
}
}

View File

@@ -159,6 +159,7 @@ import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptLogger;
@@ -514,6 +515,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mNotificationShelfController,
mStackScrollerController,
mNotificationPresenter,
new NotificationExpansionRepository(),
mDozeParameters,
mScrimController,
mLockscreenWallpaperLazy,

View File

@@ -72,7 +72,6 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.data.repository.FakePowerRepository;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.NotificationShadeWindowViewController;
import com.android.systemui.shade.ShadeControllerImpl;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.statusbar.NotificationClickNotifier;
@@ -85,6 +84,7 @@ import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorCon
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.provider.LaunchFullScreenIntentProvider;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
@@ -220,8 +220,8 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
HeadsUpManagerPhone headsUpManager = mock(HeadsUpManagerPhone.class);
NotificationLaunchAnimatorControllerProvider notificationAnimationProvider =
new NotificationLaunchAnimatorControllerProvider(
mock(NotificationShadeWindowViewController.class), mock(
NotificationListContainer.class),
new NotificationExpansionRepository(),
mock(NotificationListContainer.class),
headsUpManager,
mJankMonitor);
mNotificationActivityStarter =