Merge "Replace ShadeStateEvents.onPanelCollapsingChanged with a flow" into main
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 8e98d89..fa3e172 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -563,7 +563,6 @@
private boolean mHasLayoutedSinceDown;
private float mUpdateFlingVelocity;
private boolean mUpdateFlingOnLayout;
- private boolean mClosing;
private boolean mTouchSlopExceeded;
private int mTrackingPointer;
private int mTouchSlop;
@@ -2934,10 +2933,7 @@
@VisibleForTesting
void setClosing(boolean isClosing) {
- if (mClosing != isClosing) {
- mClosing = isClosing;
- mShadeExpansionStateManager.notifyPanelCollapsingChanged(isClosing);
- }
+ mShadeRepository.setLegacyIsClosing(isClosing);
mAmbientState.setIsClosing(isClosing);
}
@@ -3468,7 +3464,7 @@
ipw.print("mHasLayoutedSinceDown="); ipw.println(mHasLayoutedSinceDown);
ipw.print("mUpdateFlingVelocity="); ipw.println(mUpdateFlingVelocity);
ipw.print("mUpdateFlingOnLayout="); ipw.println(mUpdateFlingOnLayout);
- ipw.print("mClosing="); ipw.println(mClosing);
+ ipw.print("isClosing()="); ipw.println(isClosing());
ipw.print("mTouchSlopExceeded="); ipw.println(mTouchSlopExceeded);
ipw.print("mTrackingPointer="); ipw.println(mTrackingPointer);
ipw.print("mTouchSlop="); ipw.println(mTouchSlop);
@@ -3807,7 +3803,7 @@
}
private void endClosing() {
- if (mClosing) {
+ if (isClosing()) {
setClosing(false);
onClosingFinished();
}
@@ -3927,7 +3923,7 @@
mExpandedHeight = Math.min(h, maxPanelHeight);
// If we are closing the panel and we are almost there due to a slow decelerating
// interpolator, abort the animation.
- if (mExpandedHeight < 1f && mExpandedHeight != 0f && mClosing) {
+ if (mExpandedHeight < 1f && mExpandedHeight != 0f && isClosing()) {
mExpandedHeight = 0f;
if (mHeightAnimator != null) {
mHeightAnimator.end();
@@ -4002,7 +3998,7 @@
@Override
public boolean isCollapsing() {
- return mClosing || mIsLaunchAnimationRunning;
+ return isClosing() || mIsLaunchAnimationRunning;
}
public boolean isTracking() {
@@ -4011,7 +4007,7 @@
@Override
public boolean canBeCollapsed() {
- return !isFullyCollapsed() && !isTracking() && !mClosing;
+ return !isFullyCollapsed() && !isTracking() && !isClosing();
}
@Override
@@ -4126,7 +4122,7 @@
@VisibleForTesting
boolean isClosing() {
- return mClosing;
+ return mShadeRepository.getLegacyIsClosing().getValue();
}
@Override
@@ -4839,11 +4835,11 @@
mAnimatingOnDown = mHeightAnimator != null && !mIsSpringBackAnimation;
mMinExpandHeight = 0.0f;
mDownTime = mSystemClock.uptimeMillis();
- if (mAnimatingOnDown && mClosing) {
+ if (mAnimatingOnDown && isClosing()) {
cancelHeightAnimator();
mTouchSlopExceeded = true;
mShadeLog.v("NotificationPanelViewController MotionEvent intercepted:"
- + " mAnimatingOnDown: true, mClosing: true");
+ + " mAnimatingOnDown: true, isClosing(): true");
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt
index 53eccfd..832fefc 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt
@@ -17,6 +17,8 @@
package com.android.systemui.shade
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor
+import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractorEmptyImpl
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.domain.interactor.ShadeInteractorEmptyImpl
import dagger.Binds
@@ -36,4 +38,10 @@
@Binds
@SysUISingleton
abstract fun bindsShadeInteractor(si: ShadeInteractorEmptyImpl): ShadeInteractor
+
+ @Binds
+ @SysUISingleton
+ abstract fun bindsShadeAnimationInteractor(
+ sai: ShadeAnimationInteractorEmptyImpl
+ ): ShadeAnimationInteractor
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
index e20534c..d6db19e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
@@ -163,12 +163,6 @@
}
}
- fun notifyPanelCollapsingChanged(isCollapsing: Boolean) {
- for (cb in shadeStateEventsListeners) {
- cb.onPanelCollapsingChanged(isCollapsing)
- }
- }
-
private fun debugLog(msg: String) {
if (!DEBUG) return
Log.v(TAG, msg)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
index 54467cf..d9b298d 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
@@ -19,6 +19,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.scene.shared.flag.SceneContainerFlags
import com.android.systemui.shade.domain.interactor.BaseShadeInteractor
+import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor
+import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractorLegacyImpl
+import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractorSceneContainerImpl
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.domain.interactor.ShadeInteractorImpl
import com.android.systemui.shade.domain.interactor.ShadeInteractorLegacyImpl
@@ -45,6 +48,20 @@
sceneContainerOff.get()
}
}
+
+ @Provides
+ @SysUISingleton
+ fun provideShadeAnimationInteractor(
+ sceneContainerFlags: SceneContainerFlags,
+ sceneContainerOn: Provider<ShadeAnimationInteractorSceneContainerImpl>,
+ sceneContainerOff: Provider<ShadeAnimationInteractorLegacyImpl>
+ ): ShadeAnimationInteractor {
+ return if (sceneContainerFlags.isEnabled()) {
+ sceneContainerOn.get()
+ } else {
+ sceneContainerOff.get()
+ }
+ }
}
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt
index c8511d7..ff96ca3c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt
@@ -27,10 +27,6 @@
/** Callbacks for certain notification panel events. */
interface ShadeStateEventsListener {
-
- /** Invoked when the notification panel starts or stops collapsing. */
- fun onPanelCollapsingChanged(isCollapsing: Boolean) {}
-
/**
* Invoked when the notification panel starts or stops launching an [android.app.Activity].
*/
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
index 47b08fe..e94a3eb 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
@@ -105,6 +105,12 @@
/** True when QS is taking up the entire screen, i.e. fully expanded on a non-unfolded phone. */
@Deprecated("Use ShadeInteractor instead") val legacyQsFullscreen: StateFlow<Boolean>
+ /** NPVC.mClosing as a flow. */
+ @Deprecated("Use ShadeAnimationInteractor instead") val legacyIsClosing: StateFlow<Boolean>
+
+ /** Sets whether a closing animation is happening. */
+ @Deprecated("Use ShadeAnimationInteractor instead") fun setLegacyIsClosing(isClosing: Boolean)
+
/** */
@Deprecated("Use ShadeInteractor instead")
fun setLegacyQsFullscreen(legacyQsFullscreen: Boolean)
@@ -261,6 +267,15 @@
_legacyShadeTracking.value = tracking
}
+ private val _legacyIsClosing = MutableStateFlow(false)
+ @Deprecated("Use ShadeInteractor instead")
+ override val legacyIsClosing: StateFlow<Boolean> = _legacyIsClosing.asStateFlow()
+
+ @Deprecated("Use ShadeInteractor instead")
+ override fun setLegacyIsClosing(isClosing: Boolean) {
+ _legacyIsClosing.value = isClosing
+ }
+
@Deprecated("Should only be called by NPVC and tests")
override fun setLegacyLockscreenShadeTracking(tracking: Boolean) {
legacyLockscreenShadeTracking.value = tracking
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractor.kt
new file mode 100644
index 0000000..ff422b7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractor.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.shade.domain.interactor
+
+import kotlinx.coroutines.flow.Flow
+
+/** Business logic related to shade animations and transitions. */
+interface ShadeAnimationInteractor {
+ /**
+ * Whether a short animation to close the shade or QS is running. This will be false if the user
+ * is manually closing the shade or QS but true if they lift their finger and an animation
+ * completes the close. Important: if QS is collapsing back to shade, this will be false because
+ * that is not considered "closing".
+ */
+ val isAnyCloseAnimationRunning: Flow<Boolean>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorEmptyImpl.kt
new file mode 100644
index 0000000..b4a134f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorEmptyImpl.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.shade.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.flow.flowOf
+
+/** Implementation of ShadeAnimationInteractor for shadeless SysUI variants. */
+@SysUISingleton
+class ShadeAnimationInteractorEmptyImpl @Inject constructor() : ShadeAnimationInteractor {
+ override val isAnyCloseAnimationRunning = flowOf(false)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorLegacyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorLegacyImpl.kt
new file mode 100644
index 0000000..d514093
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorLegacyImpl.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.shade.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.shade.data.repository.ShadeRepository
+import javax.inject.Inject
+
+/** Implementation of ShadeAnimationInteractor compatible with NPVC. */
+@SysUISingleton
+class ShadeAnimationInteractorLegacyImpl
+@Inject
+constructor(
+ shadeRepository: ShadeRepository,
+) : ShadeAnimationInteractor {
+ override val isAnyCloseAnimationRunning = shadeRepository.legacyIsClosing
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImpl.kt
new file mode 100644
index 0000000..7c0762d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImpl.kt
@@ -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.shade.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.model.ObservableTransitionState
+import com.android.systemui.scene.shared.model.SceneKey
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+
+/** Implementation of ShadeAnimationInteractor compatible with the scene container framework. */
+@SysUISingleton
+class ShadeAnimationInteractorSceneContainerImpl
+@Inject
+constructor(
+ sceneInteractor: SceneInteractor,
+) : ShadeAnimationInteractor {
+ @OptIn(ExperimentalCoroutinesApi::class)
+ override val isAnyCloseAnimationRunning =
+ sceneInteractor.transitionState
+ .flatMapLatest { state ->
+ when (state) {
+ is ObservableTransitionState.Idle -> flowOf(false)
+ is ObservableTransitionState.Transition ->
+ if (
+ (state.fromScene == SceneKey.Shade &&
+ state.toScene != SceneKey.QuickSettings) ||
+ (state.fromScene == SceneKey.QuickSettings &&
+ state.toScene != SceneKey.Shade)
+ ) {
+ state.isUserInputOngoing.map { !it }
+ } else {
+ flowOf(false)
+ }
+ }
+ }
+ .distinctUntilChanged()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
index 7cff8ea..3fd070c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
@@ -71,14 +71,11 @@
override val isQsBypassingShade: Flow<Boolean> =
sceneInteractor.transitionState
- .flatMapLatest { state ->
+ .map { state ->
when (state) {
- is ObservableTransitionState.Idle -> flowOf(false)
+ is ObservableTransitionState.Idle -> false
is ObservableTransitionState.Transition ->
- flowOf(
- state.toScene == SceneKey.QuickSettings &&
- state.fromScene != SceneKey.Shade
- )
+ state.toScene == SceneKey.QuickSettings && state.fromScene != SceneKey.Shade
}
}
.distinctUntilChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index a2379b2..46e2391 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -29,6 +29,7 @@
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeStateEvents;
+import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.ListEntry;
@@ -39,6 +40,7 @@
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.Compile;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.kotlin.JavaAdapter;
import java.io.PrintWriter;
import java.util.HashMap;
@@ -62,7 +64,9 @@
private final DelayableExecutor mDelayableExecutor;
private final HeadsUpManager mHeadsUpManager;
private final ShadeStateEvents mShadeStateEvents;
+ private final ShadeAnimationInteractor mShadeAnimationInteractor;
private final StatusBarStateController mStatusBarStateController;
+ private final JavaAdapter mJavaAdapter;
private final VisibilityLocationProvider mVisibilityLocationProvider;
private final VisualStabilityProvider mVisualStabilityProvider;
private final WakefulnessLifecycle mWakefulnessLifecycle;
@@ -95,11 +99,15 @@
DumpManager dumpManager,
HeadsUpManager headsUpManager,
ShadeStateEvents shadeStateEvents,
+ ShadeAnimationInteractor shadeAnimationInteractor,
+ JavaAdapter javaAdapter,
StatusBarStateController statusBarStateController,
VisibilityLocationProvider visibilityLocationProvider,
VisualStabilityProvider visualStabilityProvider,
WakefulnessLifecycle wakefulnessLifecycle) {
mHeadsUpManager = headsUpManager;
+ mShadeAnimationInteractor = shadeAnimationInteractor;
+ mJavaAdapter = javaAdapter;
mVisibilityLocationProvider = visibilityLocationProvider;
mVisualStabilityProvider = visualStabilityProvider;
mWakefulnessLifecycle = wakefulnessLifecycle;
@@ -119,6 +127,8 @@
mStatusBarStateController.addCallback(mStatusBarStateControllerListener);
mPulsing = mStatusBarStateController.isPulsing();
mShadeStateEvents.addShadeStateEventsListener(this);
+ mJavaAdapter.alwaysCollectFlow(mShadeAnimationInteractor.isAnyCloseAnimationRunning(),
+ this::onShadeOrQsClosingChanged);
pipeline.setVisualStabilityManager(mNotifStabilityManager);
}
@@ -322,10 +332,9 @@
}
}
- @Override
- public void onPanelCollapsingChanged(boolean isCollapsing) {
- mNotifPanelCollapsing = isCollapsing;
- updateAllowedStates("notifPanelCollapsing", isCollapsing);
+ private void onShadeOrQsClosingChanged(boolean isClosing) {
+ mNotifPanelCollapsing = isClosing;
+ updateAllowedStates("notifPanelCollapsing", isClosing);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
index 5f8777d..f8aa359 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
@@ -225,4 +225,13 @@
underTest.setLegacyQsFullscreen(true)
assertThat(underTest.legacyQsFullscreen.value).isEqualTo(true)
}
+
+ @Test
+ fun updateLegacyIsClosing() =
+ testScope.runTest {
+ assertThat(underTest.legacyIsClosing.value).isEqualTo(false)
+
+ underTest.setLegacyIsClosing(true)
+ assertThat(underTest.legacyIsClosing.value).isEqualTo(true)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImplTest.kt
new file mode 100644
index 0000000..40006ba
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImplTest.kt
@@ -0,0 +1,149 @@
+/*
+ * 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.shade.domain.interactor
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.flags.Flags
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.model.ObservableTransitionState
+import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.statusbar.phone.DozeParameters
+import com.android.systemui.user.domain.UserDomainLayerModule
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth
+import dagger.BindsInstance
+import dagger.Component
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
+import org.junit.Test
+
+@SmallTest
+class ShadeAnimationInteractorSceneContainerImplTest : SysuiTestCase() {
+
+ @SysUISingleton
+ @Component(
+ modules =
+ [
+ SysUITestModule::class,
+ UserDomainLayerModule::class,
+ ]
+ )
+ interface TestComponent : SysUITestComponent<ShadeAnimationInteractorSceneContainerImpl> {
+ val sceneInteractor: SceneInteractor
+
+ @Component.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance test: SysuiTestCase,
+ featureFlags: FakeFeatureFlagsClassicModule,
+ mocks: TestMocksModule,
+ ): TestComponent
+ }
+ }
+
+ private val dozeParameters: DozeParameters = mock()
+
+ private val testComponent: TestComponent =
+ DaggerShadeAnimationInteractorSceneContainerImplTest_TestComponent.factory()
+ .create(
+ test = this,
+ featureFlags =
+ FakeFeatureFlagsClassicModule { set(Flags.FULL_SCREEN_USER_SWITCHER, true) },
+ mocks =
+ TestMocksModule(
+ dozeParameters = dozeParameters,
+ ),
+ )
+
+ @Test
+ fun isAnyCloseAnimationRunning_qsToShade() =
+ testComponent.runTest() {
+ val actual by collectLastValue(underTest.isAnyCloseAnimationRunning)
+
+ // WHEN transitioning from QS to Shade
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = SceneKey.QuickSettings,
+ toScene = SceneKey.Shade,
+ progress = MutableStateFlow(.1f),
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+ runCurrent()
+
+ // THEN qs is animating closed
+ Truth.assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun isAnyCloseAnimationRunning_qsToGone_userInputNotOngoing() =
+ testComponent.runTest() {
+ val actual by collectLastValue(underTest.isAnyCloseAnimationRunning)
+
+ // WHEN transitioning from QS to Gone with no ongoing user input
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = SceneKey.QuickSettings,
+ toScene = SceneKey.Gone,
+ progress = MutableStateFlow(.1f),
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+ runCurrent()
+
+ // THEN qs is animating closed
+ Truth.assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun isAnyCloseAnimationRunning_qsToGone_userInputOngoing() =
+ testComponent.runTest() {
+ val actual by collectLastValue(underTest.isAnyCloseAnimationRunning)
+
+ // WHEN transitioning from QS to Gone with user input ongoing
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = SceneKey.QuickSettings,
+ toScene = SceneKey.Gone,
+ progress = MutableStateFlow(.1f),
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(true),
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+ runCurrent()
+
+ // THEN qs is not animating closed
+ Truth.assertThat(actual).isFalse()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
index 565e20a..310b86f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
@@ -127,22 +127,22 @@
val actual by collectLastValue(underTest.qsExpansion)
// WHEN split shade is enabled and QS is expanded
- keyguardRepository.setStatusBarState(StatusBarState.SHADE)
overrideResource(R.bool.config_use_split_notification_shade, true)
configurationRepository.onAnyConfigurationChange()
- val progress = MutableStateFlow(.3f)
+ runCurrent()
val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Transition(
fromScene = SceneKey.QuickSettings,
toScene = SceneKey.Shade,
- progress = progress,
+ progress = MutableStateFlow(.3f),
isInitiatedByUserInput = false,
isUserInputOngoing = flowOf(false),
)
)
sceneInteractor.setTransitionState(transitionState)
runCurrent()
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
// THEN legacy shade expansion is passed through
Truth.assertThat(actual).isEqualTo(.3f)
@@ -157,6 +157,8 @@
// WHEN split shade is not enabled and QS is expanded
keyguardRepository.setStatusBarState(StatusBarState.SHADE)
overrideResource(R.bool.config_use_split_notification_shade, false)
+ configurationRepository.onAnyConfigurationChange()
+ runCurrent()
val progress = MutableStateFlow(.3f)
val transitionState =
MutableStateFlow<ObservableTransitionState>(
@@ -182,13 +184,12 @@
// WHEN scene transition active
keyguardRepository.setStatusBarState(StatusBarState.SHADE)
- val progress = MutableStateFlow(.3f)
val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Transition(
fromScene = SceneKey.QuickSettings,
toScene = SceneKey.Shade,
- progress = progress,
+ progress = MutableStateFlow(.3f),
isInitiatedByUserInput = false,
isUserInputOngoing = flowOf(false),
)
@@ -347,6 +348,52 @@
Truth.assertThat(expansionAmount).isEqualTo(0f)
}
+ fun isQsBypassingShade_goneToQs() =
+ testComponent.runTest() {
+ val actual by collectLastValue(underTest.isQsBypassingShade)
+
+ // WHEN transitioning from QS directly to Gone
+ configurationRepository.onAnyConfigurationChange()
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = SceneKey.Gone,
+ toScene = SceneKey.QuickSettings,
+ progress = MutableStateFlow(.1f),
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+ runCurrent()
+
+ // THEN qs is bypassing shade
+ Truth.assertThat(actual).isTrue()
+ }
+
+ fun isQsBypassingShade_shadeToQs() =
+ testComponent.runTest() {
+ val actual by collectLastValue(underTest.isQsBypassingShade)
+
+ // WHEN transitioning from QS to Shade
+ configurationRepository.onAnyConfigurationChange()
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = SceneKey.Shade,
+ toScene = SceneKey.QuickSettings,
+ progress = MutableStateFlow(.1f),
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ sceneInteractor.setTransitionState(transitionState)
+ runCurrent()
+
+ // THEN qs is not bypassing shade
+ Truth.assertThat(actual).isFalse()
+ }
+
@Test
fun lockscreenShadeExpansion_transitioning_toAndFromDifferentScenes() =
testComponent.runTest() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
index e488f39..bd46474 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
@@ -34,12 +34,14 @@
import androidx.test.filters.SmallTest;
+import com.android.keyguard.TestScopeProvider;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeStateEvents;
import com.android.systemui.shade.ShadeStateEvents.ShadeStateEventsListener;
+import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder;
@@ -51,6 +53,7 @@
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
@@ -62,6 +65,10 @@
import org.mockito.MockitoAnnotations;
import org.mockito.verification.VerificationMode;
+import kotlinx.coroutines.flow.MutableStateFlow;
+import kotlinx.coroutines.flow.StateFlowKt;
+import kotlinx.coroutines.test.TestScope;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -78,6 +85,7 @@
@Mock private ShadeStateEvents mShadeStateEvents;
@Mock private VisibilityLocationProvider mVisibilityLocationProvider;
@Mock private VisualStabilityProvider mVisualStabilityProvider;
+ @Mock private ShadeAnimationInteractor mShadeAnimationInteractor;
@Captor private ArgumentCaptor<WakefulnessLifecycle.Observer> mWakefulnessObserverCaptor;
@Captor private ArgumentCaptor<StatusBarStateController.StateListener> mSBStateListenerCaptor;
@@ -86,6 +94,9 @@
private FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock);
+ private final TestScope mTestScope = TestScopeProvider.getTestScope();
+ private final JavaAdapter mJavaAdapter = new JavaAdapter(mTestScope.getBackgroundScope());
+ private final MutableStateFlow<Boolean> mShadeClosing = StateFlowKt.MutableStateFlow(false);
private WakefulnessLifecycle.Observer mWakefulnessObserver;
private StatusBarStateController.StateListener mStatusBarStateListener;
@@ -103,11 +114,13 @@
mDumpManager,
mHeadsUpManager,
mShadeStateEvents,
+ mShadeAnimationInteractor,
+ mJavaAdapter,
mStatusBarStateController,
mVisibilityLocationProvider,
mVisualStabilityProvider,
mWakefulnessLifecycle);
-
+ when(mShadeAnimationInteractor.isAnyCloseAnimationRunning()).thenReturn(mShadeClosing);
mCoordinator.attach(mNotifPipeline);
// capture arguments:
@@ -549,7 +562,8 @@
}
private void setPanelCollapsing(boolean collapsing) {
- mNotifPanelEventsCallback.onPanelCollapsingChanged(collapsing);
+ mShadeClosing.setValue(collapsing);
+ mTestScope.getTestScheduler().runCurrent();
}
private void setPulsing(boolean pulsing) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
index a70b91d..9c10848 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
@@ -106,6 +106,14 @@
_legacyQsFullscreen.value = legacyQsFullscreen
}
+ private val _legacyIsClosing = MutableStateFlow(false)
+ @Deprecated("Use ShadeInteractor instead") override val legacyIsClosing = _legacyIsClosing
+
+ @Deprecated("Use ShadeInteractor instead")
+ override fun setLegacyIsClosing(isClosing: Boolean) {
+ _legacyIsClosing.value = isClosing
+ }
+
fun setShadeModel(model: ShadeModel) {
_shadeModel.value = model
}