diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java index ef14d1cb7f63d..4012736b8beb0 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java @@ -70,6 +70,7 @@ import com.android.systemui.media.controls.ui.MediaHierarchyManager; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QS; import com.android.systemui.screenrecord.RecordingController; +import com.android.systemui.shade.data.repository.ShadeRepository; import com.android.systemui.shade.transition.ShadeTransitionController; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.LockscreenShadeTransitionController; @@ -136,6 +137,7 @@ public class QuickSettingsController { private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor; private final FeatureFlags mFeatureFlags; private final InteractionJankMonitor mInteractionJankMonitor; + private final ShadeRepository mShadeRepository; private final FalsingManager mFalsingManager; private final AccessibilityManager mAccessibilityManager; private final MetricsLogger mMetricsLogger; @@ -321,7 +323,8 @@ public class QuickSettingsController { FeatureFlags featureFlags, InteractionJankMonitor interactionJankMonitor, ShadeLogger shadeLog, - KeyguardFaceAuthInteractor keyguardFaceAuthInteractor + KeyguardFaceAuthInteractor keyguardFaceAuthInteractor, + ShadeRepository shadeRepository ) { mPanelViewControllerLazy = panelViewControllerLazy; mPanelView = panelView; @@ -363,6 +366,7 @@ public class QuickSettingsController { mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor; mFeatureFlags = featureFlags; mInteractionJankMonitor = interactionJankMonitor; + mShadeRepository = shadeRepository; mLockscreenShadeTransitionController.addCallback(new LockscreenShadeTransitionCallback()); } @@ -1001,6 +1005,7 @@ public class QuickSettingsController { mDepthController.setQsPanelExpansion(qsExpansionFraction); mStatusBarKeyguardViewManager.setQsExpansion(qsExpansionFraction); + mShadeRepository.setQsExpansion(qsExpansionFraction); // TODO (b/265193930): remove dependency on NPVC float shadeExpandedFraction = mBarState == KEYGUARD 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 bf7a2be2e4ca2..44c8e48c65d47 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 @@ -25,11 +25,23 @@ import com.android.systemui.shade.domain.model.ShadeModel import javax.inject.Inject import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.distinctUntilChanged interface ShadeRepository { /** ShadeModel information regarding shade expansion events */ val shadeModel: Flow + + /** Amount qs has expanded. Quick Settings can be expanded without the full shade expansion. */ + val qsExpansion: StateFlow + + /** Amount shade has expanded with regard to the UDFPS location */ + val udfpsTransitionToFullShadeProgress: StateFlow + + fun setQsExpansion(qsExpansion: Float) + fun setUdfpsTransitionToFullShadeProgress(progress: Float) } /** Business logic for shade interactions */ @@ -62,6 +74,20 @@ constructor(shadeExpansionStateManager: ShadeExpansionStateManager) : ShadeRepos } .distinctUntilChanged() + private val _qsExpansion = MutableStateFlow(0f) + override val qsExpansion: StateFlow = _qsExpansion.asStateFlow() + + private var _udfpsTransitionToFullShadeProgress = MutableStateFlow(0f) + override val udfpsTransitionToFullShadeProgress: StateFlow = + _udfpsTransitionToFullShadeProgress.asStateFlow() + override fun setQsExpansion(qsExpansion: Float) { + _qsExpansion.value = qsExpansion + } + + override fun setUdfpsTransitionToFullShadeProgress(progress: Float) { + _udfpsTransitionToFullShadeProgress.value = progress + } + companion object { private const val TAG = "ShadeRepository" } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt index c5f64b0a23063..7f016f304e05e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt @@ -32,6 +32,7 @@ import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.qs.QS import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.shade.ShadeViewController +import com.android.systemui.shade.data.repository.ShadeRepository import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView @@ -74,6 +75,7 @@ class LockscreenShadeTransitionController @Inject constructor( falsingManager: FalsingManager, dumpManager: DumpManager, qsTransitionControllerFactory: LockscreenShadeQsTransitionController.Factory, + private val shadeRepository: ShadeRepository, ) : Dumpable { private var pulseHeight: Float = 0f @get:VisibleForTesting @@ -449,6 +451,7 @@ class LockscreenShadeTransitionController @Inject constructor( } val udfpsProgress = MathUtils.saturate(dragDownAmount / udfpsTransitionDistance) + shadeRepository.setUdfpsTransitionToFullShadeProgress(udfpsProgress) udfpsKeyguardViewController?.setTransitionToFullShadeProgress(udfpsProgress) val statusBarProgress = MathUtils.saturate(dragDownAmount / statusBarTransitionDistance) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java index f870631bd72bd..1edc63c4417e2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -115,6 +115,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QS; import com.android.systemui.qs.QSFragment; import com.android.systemui.screenrecord.RecordingController; +import com.android.systemui.shade.data.repository.ShadeRepository; import com.android.systemui.shade.transition.ShadeTransitionController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.KeyguardIndicationController; @@ -304,6 +305,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mEmptySpaceClickListenerCaptor; @Mock protected ActivityStarter mActivityStarter; @Mock protected KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor; + @Mock protected ShadeRepository mShadeRepository; protected final int mMaxUdfpsBurnInOffsetY = 5; protected KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor; @@ -672,7 +674,8 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mFeatureFlags, mInteractionJankMonitor, mShadeLog, - mKeyguardFaceAuthInteractor + mKeyguardFaceAuthInteractor, + mShadeRepository ); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java index 908f7cbf48015..8a9161e6d46db 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java @@ -69,6 +69,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QS; import com.android.systemui.qs.QSFragment; import com.android.systemui.screenrecord.RecordingController; +import com.android.systemui.shade.data.repository.ShadeRepository; import com.android.systemui.shade.transition.ShadeTransitionController; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.NotificationRemoteInputManager; @@ -88,6 +89,8 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager; import com.android.systemui.statusbar.policy.KeyguardStateController; +import dagger.Lazy; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -98,8 +101,6 @@ import org.mockito.MockitoAnnotations; import java.util.List; -import dagger.Lazy; - @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @@ -241,7 +242,8 @@ public class QuickSettingsControllerTest extends SysuiTestCase { mFeatureFlags, mInteractionJankMonitor, mShadeLogger, - mock(KeyguardFaceAuthInteractor.class) + mock(KeyguardFaceAuthInteractor.class), + mock(ShadeRepository.class) ); mFragmentListener = mQsController.getQsFragmentListener(); 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 new file mode 100644 index 0000000000000..8f2c93b3bf43f --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt @@ -0,0 +1,133 @@ +/* + * 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.data.repository + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.RoboPilotTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.shade.ShadeExpansionChangeEvent +import com.android.systemui.shade.ShadeExpansionStateManager +import com.android.systemui.shade.domain.model.ShadeModel +import com.android.systemui.util.mockito.withArgCaptor +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RoboPilotTest +@RunWith(AndroidJUnit4::class) +class ShadeRepositoryImplTest : SysuiTestCase() { + + @Mock private lateinit var shadeExpansionStateManager: ShadeExpansionStateManager + private val testDispatcher = StandardTestDispatcher() + private val testScope = TestScope(testDispatcher) + + private lateinit var underTest: ShadeRepositoryImpl + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + underTest = ShadeRepositoryImpl(shadeExpansionStateManager) + } + + @Test + fun shadeExpansionChangeEvent() = + testScope.runTest { + var latest: ShadeModel? = null + val job = underTest.shadeModel.onEach { latest = it }.launchIn(this) + runCurrent() + assertThat(latest?.expansionAmount).isEqualTo(0f) + assertThat(latest?.isExpanded).isEqualTo(false) + assertThat(latest?.isUserDragging).isEqualTo(false) + + val captor = withArgCaptor { + verify(shadeExpansionStateManager).addExpansionListener(capture()) + } + + captor.onPanelExpansionChanged( + ShadeExpansionChangeEvent( + fraction = 1f, + expanded = true, + tracking = false, + dragDownPxAmount = 0f, + ) + ) + runCurrent() + assertThat(latest?.expansionAmount).isEqualTo(1f) + assertThat(latest?.isExpanded).isEqualTo(true) + assertThat(latest?.isUserDragging).isEqualTo(false) + + captor.onPanelExpansionChanged( + ShadeExpansionChangeEvent( + fraction = .67f, + expanded = false, + tracking = true, + dragDownPxAmount = 0f, + ) + ) + runCurrent() + assertThat(latest?.expansionAmount).isEqualTo(.67f) + assertThat(latest?.isExpanded).isEqualTo(false) + assertThat(latest?.isUserDragging).isEqualTo(true) + + job.cancel() + } + + @Test + fun updateQsExpansion() = + testScope.runTest { + assertThat(underTest.qsExpansion.value).isEqualTo(0f) + + underTest.setQsExpansion(.5f) + assertThat(underTest.qsExpansion.value).isEqualTo(.5f) + + underTest.setQsExpansion(.82f) + assertThat(underTest.qsExpansion.value).isEqualTo(.82f) + + underTest.setQsExpansion(1f) + assertThat(underTest.qsExpansion.value).isEqualTo(1f) + } + + @Test + fun updateUdfpsTransitionToFullShadeProgress() = + testScope.runTest { + assertThat(underTest.udfpsTransitionToFullShadeProgress.value).isEqualTo(0f) + + underTest.setUdfpsTransitionToFullShadeProgress(.5f) + assertThat(underTest.udfpsTransitionToFullShadeProgress.value).isEqualTo(.5f) + + underTest.setUdfpsTransitionToFullShadeProgress(.82f) + assertThat(underTest.udfpsTransitionToFullShadeProgress.value).isEqualTo(.82f) + + underTest.setUdfpsTransitionToFullShadeProgress(1f) + assertThat(underTest.udfpsTransitionToFullShadeProgress.value).isEqualTo(1f) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt index 2106da887b444..2351f7600c65d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt @@ -15,6 +15,7 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.qs.QS import com.android.systemui.shade.ShadeViewController +import com.android.systemui.shade.data.repository.FakeShadeRepository import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationTestHelper import com.android.systemui.statusbar.notification.stack.AmbientState @@ -127,6 +128,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { }, qsTransitionControllerFactory = { qsTransitionController }, activityStarter = activityStarter, + shadeRepository = FakeShadeRepository(), ) transitionController.addCallback(transitionControllerCallback) whenever(nsslController.view).thenReturn(stackscroller) 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 2c0a8fde0d77a..492e5421b0103 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 @@ -21,13 +21,27 @@ import com.android.systemui.shade.domain.model.ShadeModel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -/** Fake implementation of [KeyguardRepository] */ +/** Fake implementation of [ShadeRepository] */ class FakeShadeRepository : ShadeRepository { private val _shadeModel = MutableStateFlow(ShadeModel()) override val shadeModel: Flow = _shadeModel + private val _qsExpansion = MutableStateFlow(0f) + override val qsExpansion = _qsExpansion + + private val _udfpsTransitionToFullShadeProgress = MutableStateFlow(0f) + override val udfpsTransitionToFullShadeProgress = _udfpsTransitionToFullShadeProgress + fun setShadeModel(model: ShadeModel) { _shadeModel.value = model } + + override fun setQsExpansion(qsExpansion: Float) { + _qsExpansion.value = qsExpansion + } + + override fun setUdfpsTransitionToFullShadeProgress(progress: Float) { + _udfpsTransitionToFullShadeProgress.value = progress + } }