Merge changes from topic "374333334" into main
* changes:
Implement getQsMinExpansionHeightForSplitShade
Brightness dialog use new composable
Support Brightness mirror
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 8ddd922..a18b6c1 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -97,6 +97,7 @@
"tests/src/**/systemui/media/dialog/MediaOutputBroadcastDialogTest.java",
"tests/src/**/systemui/media/dialog/MediaOutputDialogTest.java",
"tests/src/**/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt",
+ "tests/src/**/systemui/settings/brightness/BrightnessDialogTest.kt",
],
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt
index 18f33e4..116b705 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt
@@ -30,16 +30,20 @@
import com.android.systemui.common.shared.model.Text
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory
-import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
import com.android.systemui.res.R
+import com.android.systemui.settings.brightness.domain.interactor.brightnessMirrorShowingInteractor
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class BrightnessSliderViewModelTest : SysuiTestCase() {
@@ -49,15 +53,17 @@
private val kosmos = testKosmos()
- private val underTest =
+ private val underTest by lazy {
with(kosmos) {
BrightnessSliderViewModel(
screenBrightnessInteractor,
brightnessPolicyEnforcementInteractor,
- applicationCoroutineScope,
sliderHapticsViewModelFactory,
+ brightnessMirrorShowingInteractor,
+ supportsMirroring = true,
)
}
+ }
@Before
fun setUp() {
@@ -65,18 +71,18 @@
LinearBrightness(minBrightness),
LinearBrightness(maxBrightness),
)
+ underTest.activateIn(kosmos.testScope)
}
@Test
fun brightnessChangeInRepository_changeInFlow() =
with(kosmos) {
testScope.runTest {
- val gammaBrightness by collectLastValue(underTest.currentBrightness)
-
var brightness = 0.6f
fakeScreenBrightnessRepository.setBrightness(LinearBrightness(brightness))
+ runCurrent()
- assertThat(gammaBrightness!!.value)
+ assertThat(underTest.currentBrightness.value)
.isEqualTo(
BrightnessUtils.convertLinearToGammaFloat(
brightness,
@@ -87,8 +93,9 @@
brightness = 0.2f
fakeScreenBrightnessRepository.setBrightness(LinearBrightness(brightness))
+ runCurrent()
- assertThat(gammaBrightness!!.value)
+ assertThat(underTest.currentBrightness.value)
.isEqualTo(
BrightnessUtils.convertLinearToGammaFloat(
brightness,
@@ -117,7 +124,6 @@
testScope.runTest {
val temporaryBrightness by
collectLastValue(fakeScreenBrightnessRepository.temporaryBrightness)
- val brightness by collectLastValue(underTest.currentBrightness)
val newBrightness = underTest.maxBrightness.value / 3
val expectedTemporaryBrightness =
@@ -133,7 +139,7 @@
assertThat(temporaryBrightness!!.floatValue)
.isWithin(1e-5f)
.of(expectedTemporaryBrightness)
- assertThat(brightness!!.value).isNotEqualTo(newBrightness)
+ assertThat(underTest.currentBrightness.value).isNotEqualTo(newBrightness)
}
}
@@ -141,14 +147,13 @@
fun draggingStopped_currentBrightnessChanges() =
with(kosmos) {
testScope.runTest {
- val brightness by collectLastValue(underTest.currentBrightness)
-
val newBrightness = underTest.maxBrightness.value / 3
val drag = Drag.Stopped(GammaBrightness(newBrightness))
underTest.onDrag(drag)
+ runCurrent()
- assertThat(brightness!!.value).isEqualTo(newBrightness)
+ assertThat(underTest.currentBrightness.value).isEqualTo(newBrightness)
}
}
@@ -168,4 +173,40 @@
)
)
}
+
+ @Test
+ fun supportedMirror_mirrorShowingWhenDragging() =
+ with(kosmos) {
+ testScope.runTest {
+ val mirrorInInteractor by
+ collectLastValue(brightnessMirrorShowingInteractor.isShowing)
+
+ underTest.setIsDragging(true)
+ assertThat(mirrorInInteractor).isEqualTo(true)
+ assertThat(underTest.showMirror).isEqualTo(true)
+
+ underTest.setIsDragging(false)
+ assertThat(mirrorInInteractor).isEqualTo(false)
+ assertThat(underTest.showMirror).isEqualTo(false)
+ }
+ }
+
+ @Test
+ fun unsupportedMirror_mirrorNeverShowing() =
+ with(kosmos) {
+ testScope.runTest {
+ val mirrorInInteractor by
+ collectLastValue(brightnessMirrorShowingInteractor.isShowing)
+
+ val noMirrorViewModel = brightnessSliderViewModelFactory.create(false)
+
+ noMirrorViewModel.setIsDragging(true)
+ assertThat(mirrorInInteractor).isEqualTo(false)
+ assertThat(noMirrorViewModel.showMirror).isEqualTo(false)
+
+ noMirrorViewModel.setIsDragging(false)
+ assertThat(mirrorInInteractor).isEqualTo(false)
+ assertThat(noMirrorViewModel.showMirror).isEqualTo(false)
+ }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt
index fd1c043..c3a777c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt
@@ -28,6 +28,7 @@
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.kosmos.testScope
import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.qs.panels.ui.viewmodel.editModeViewModel
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge
import com.android.systemui.testKosmos
@@ -62,8 +63,7 @@
fun back_notEditing_hidesShade() =
testScope.runTest {
val actions by collectLastValue(underTest.actions)
- val isEditing by
- collectLastValue(kosmos.quickSettingsContainerViewModel.editModeViewModel.isEditing)
+ val isEditing by collectLastValue(kosmos.editModeViewModel.isEditing)
underTest.activateIn(this)
assertThat(isEditing).isFalse()
@@ -77,7 +77,7 @@
val actions by collectLastValue(underTest.actions)
underTest.activateIn(this)
- kosmos.quickSettingsContainerViewModel.editModeViewModel.startEditing()
+ kosmos.editModeViewModel.startEditing()
assertThat(actions?.get(Back)).isNull()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelTest.kt
deleted file mode 100644
index 32772d2..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelTest.kt
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (C) 2024 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.qs.ui.viewmodel
-
-import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.compose.animation.scene.Back
-import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.UserActionResult
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
-import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
-import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
-import com.android.systemui.flags.EnableSceneContainer
-import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
-import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
-import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.lifecycle.activateIn
-import com.android.systemui.qs.panels.ui.viewmodel.editModeViewModel
-import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
-import com.android.systemui.scene.shared.model.SceneFamilies
-import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.testKosmos
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper
-@EnableSceneContainer
-class QuickSettingsShadeUserActionsViewModelTest : SysuiTestCase() {
-
- private val kosmos = testKosmos()
- private val testScope = kosmos.testScope
- private val sceneInteractor = kosmos.sceneInteractor
- private val deviceUnlockedInteractor = kosmos.deviceUnlockedInteractor
-
- private val underTest by lazy { kosmos.quickSettingsShadeUserActionsViewModel }
-
- @Test
- fun upTransitionSceneKey_deviceLocked_lockscreen() =
- testScope.runTest {
- underTest.activateIn(this)
- val actions by collectLastValue(underTest.actions)
- val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
- lockDevice()
-
- assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
- .isEqualTo(SceneFamilies.Home)
- assertThat(actions?.get(Swipe.Down)).isNull()
- assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
- }
-
- @Test
- fun upTransitionSceneKey_deviceLocked_keyguardDisabled_gone() =
- testScope.runTest {
- underTest.activateIn(this)
- val actions by collectLastValue(underTest.actions)
- val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
- lockDevice()
- kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
-
- assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
- .isEqualTo(SceneFamilies.Home)
- assertThat(homeScene).isEqualTo(Scenes.Gone)
- }
-
- @Test
- fun upTransitionSceneKey_deviceUnlocked_gone() =
- testScope.runTest {
- underTest.activateIn(this)
- val actions by collectLastValue(underTest.actions)
- val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
- lockDevice()
- unlockDevice()
-
- assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
- .isEqualTo(SceneFamilies.Home)
- assertThat(actions?.get(Swipe.Down)).isNull()
- assertThat(homeScene).isEqualTo(Scenes.Gone)
- }
-
- @Test
- fun upTransitionSceneKey_authMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
- testScope.runTest {
- underTest.activateIn(this)
- val actions by collectLastValue(underTest.actions)
- val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
- kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
- kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.None
- )
- sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
-
- assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
- .isEqualTo(SceneFamilies.Home)
- assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
- }
-
- @Test
- fun upTransitionSceneKey_authMethodSwipe_lockscreenDismissed_goesToGone() =
- testScope.runTest {
- underTest.activateIn(this)
- val actions by collectLastValue(underTest.actions)
- val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
- kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
- kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.None
- )
- runCurrent()
- sceneInteractor.changeScene(Scenes.Gone, "reason")
-
- assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
- .isEqualTo(SceneFamilies.Home)
- assertThat(homeScene).isEqualTo(Scenes.Gone)
- }
-
- @Test
- fun backTransitionSceneKey_notEditing_Home() =
- testScope.runTest {
- underTest.activateIn(this)
- val actions by collectLastValue(underTest.actions)
-
- assertThat((actions?.get(Back) as? UserActionResult.ChangeScene)?.toScene)
- .isEqualTo(SceneFamilies.Home)
- }
-
- @Test
- fun backTransition_editing_noDestination() =
- testScope.runTest {
- underTest.activateIn(this)
- val actions by collectLastValue(underTest.actions)
- kosmos.editModeViewModel.startEditing()
-
- assertThat(actions!!).isNotEmpty()
- assertThat(actions?.get(Back)).isNull()
- }
-
- private fun TestScope.lockDevice() {
- val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
-
- kosmos.fakeAuthenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- assertThat(deviceUnlockStatus?.isUnlocked).isFalse()
- sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
- runCurrent()
- }
-
- private fun TestScope.unlockDevice() {
- val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
-
- kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
- SuccessFingerprintAuthenticationStatus(0, true)
- )
- assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
- sceneInteractor.changeScene(Scenes.Gone, "reason")
- runCurrent()
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 89ad699..0ff2a4a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -128,6 +128,7 @@
import com.android.systemui.qs.QSFragmentLegacy;
import com.android.systemui.res.R;
import com.android.systemui.screenrecord.RecordingController;
+import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor;
import com.android.systemui.shade.data.repository.FakeShadeRepository;
import com.android.systemui.shade.data.repository.ShadeAnimationRepository;
import com.android.systemui.shade.data.repository.ShadeRepository;
@@ -373,6 +374,9 @@
protected ShadeRepository mShadeRepository;
protected FakeMSDLPlayer mMSDLPlayer = mKosmos.getMsdlPlayer();
+ protected BrightnessMirrorShowingInteractor mBrightnessMirrorShowingInteractor =
+ mKosmos.getBrightnessMirrorShowingInteractor();
+
protected final FalsingManagerFake mFalsingManager = new FalsingManagerFake();
protected final Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent = Optional.empty();
protected final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
@@ -752,7 +756,8 @@
mPowerInteractor,
mKeyguardClockPositionAlgorithm,
mNaturalScrollingSettingObserver,
- mMSDLPlayer);
+ mMSDLPlayer,
+ mBrightnessMirrorShowingInteractor);
mNotificationPanelViewController.initDependencies(
mCentralSurfaces,
null,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 47eebf6..59d0d70 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -51,7 +51,12 @@
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.flags.QSComposeFragment
import com.android.systemui.res.R
+import com.android.systemui.settings.brightness.data.repository.BrightnessMirrorShowingRepository
+import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
import com.android.systemui.statusbar.DragDownHelper
@@ -70,6 +75,7 @@
import com.android.systemui.statusbar.phone.DozeServiceHost
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
import com.android.systemui.statusbar.window.StatusBarWindowStateController
+import com.android.systemui.testKosmos
import com.android.systemui.unfold.SysUIUnfoldComponent
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
@@ -78,11 +84,15 @@
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.util.Optional
+import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.resetMain
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.test.setMain
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
@@ -98,6 +108,7 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.clearInvocations
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
@@ -107,6 +118,8 @@
@RunWithLooper(setAsMainLooper = true)
class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : SysuiTestCase() {
+ val kosmos = testKosmos()
+
@Mock private lateinit var view: NotificationShadeWindowView
@Mock private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController
@Mock private lateinit var centralSurfaces: CentralSurfaces
@@ -148,6 +161,10 @@
private val notificationLaunchAnimationInteractor =
NotificationLaunchAnimationInteractor(notificationLaunchAnimationRepository)
+ private val brightnessMirrorShowingRepository = BrightnessMirrorShowingRepository()
+ private val brightnessMirrorShowingInteractor =
+ BrightnessMirrorShowingInteractor(brightnessMirrorShowingRepository)
+
private lateinit var falsingCollector: FalsingCollectorFake
private lateinit var fakeClock: FakeSystemClock
private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
@@ -181,8 +198,9 @@
featureFlagsClassic.set(SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true)
mSetFlagsRule.enableFlags(Flags.FLAG_REVAMPED_BOUNCER_MESSAGES)
- testScope = TestScope()
+ testScope = kosmos.testScope
testableLooper = TestableLooper.get(this)
+
falsingCollector = FalsingCollectorFake()
fakeClock = FakeSystemClock()
underTest =
@@ -221,6 +239,7 @@
alternateBouncerInteractor,
mock(BouncerViewBinder::class.java),
mock(ConfigurationForwarder::class.java),
+ brightnessMirrorShowingInteractor,
)
underTest.setupExpandedStatusBar()
underTest.setDragDownHelper(dragDownHelper)
@@ -597,6 +616,39 @@
verify(dragDownHelper).stopDragging()
}
+ @Test
+ @EnableFlags(QSComposeFragment.FLAG_NAME)
+ fun mirrorShowing_depthControllerSet() =
+ testScope.runTest {
+ try {
+ Dispatchers.setMain(kosmos.testDispatcher)
+
+ // Simulate attaching the view so flow collection starts.
+ whenever(view.viewTreeObserver).thenReturn(mock(ViewTreeObserver::class.java))
+ val onAttachStateChangeListenerArgumentCaptor =
+ ArgumentCaptor.forClass(View.OnAttachStateChangeListener::class.java)
+ verify(view, atLeast(1))
+ .addOnAttachStateChangeListener(
+ onAttachStateChangeListenerArgumentCaptor.capture()
+ )
+ for (listener in onAttachStateChangeListenerArgumentCaptor.allValues) {
+ listener.onViewAttachedToWindow(view)
+ }
+ testableLooper.processAllMessages()
+ clearInvocations(notificationShadeDepthController)
+
+ brightnessMirrorShowingInteractor.setMirrorShowing(true)
+ runCurrent()
+ verify(notificationShadeDepthController).brightnessMirrorVisible = true
+
+ brightnessMirrorShowingInteractor.setMirrorShowing(false)
+ runCurrent()
+ verify(notificationShadeDepthController).brightnessMirrorVisible = false
+ } finally {
+ Dispatchers.resetMain()
+ }
+ }
+
companion object {
private val DOWN_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
private val MOVE_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index 1c196c0..9b91fc7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -42,6 +42,8 @@
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.res.R
+import com.android.systemui.settings.brightness.data.repository.BrightnessMirrorShowingRepository
+import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
import com.android.systemui.statusbar.DragDownHelper
@@ -106,7 +108,7 @@
@Mock private lateinit var quickSettingsController: QuickSettingsController
@Mock
private lateinit var notificationStackScrollLayoutController:
- NotificationStackScrollLayoutController
+ NotificationStackScrollLayoutController
@Mock private lateinit var statusBarWindowStateController: StatusBarWindowStateController
@Mock
private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController
@@ -122,7 +124,7 @@
private lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController
@Mock
private lateinit var unfoldTransitionProgressProvider:
- Optional<UnfoldTransitionProgressProvider>
+ Optional<UnfoldTransitionProgressProvider>
@Mock private lateinit var notificationInsetsController: NotificationInsetsController
@Mock private lateinit var mGlanceableHubContainerController: GlanceableHubContainerController
@Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
@@ -132,6 +134,10 @@
@Captor
private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
+ private val brightnessMirrorShowingRepository = BrightnessMirrorShowingRepository()
+ private val brightnessMirrorShowingInteractor =
+ BrightnessMirrorShowingInteractor(brightnessMirrorShowingRepository)
+
private lateinit var underTest: NotificationShadeWindowView
private lateinit var controller: NotificationShadeWindowViewController
private lateinit var interactionEventHandler: InteractionEventHandler
@@ -142,10 +148,10 @@
MockitoAnnotations.initMocks(this)
underTest = spy(NotificationShadeWindowView(context, null))
whenever(
- underTest.findViewById<NotificationStackScrollLayout>(
- R.id.notification_stack_scroller
+ underTest.findViewById<NotificationStackScrollLayout>(
+ R.id.notification_stack_scroller
+ )
)
- )
.thenReturn(notificationStackScrollLayout)
whenever(underTest.findViewById<FrameLayout>(R.id.keyguard_bouncer_container))
.thenReturn(mock())
@@ -198,6 +204,7 @@
alternateBouncerInteractor,
mock(),
configurationForwarder,
+ brightnessMirrorShowingInteractor,
)
controller.setupExpandedStatusBar()
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 20e70e0..88ed4e3 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -286,4 +286,6 @@
<item type="id" name="snapshot_view_binding" />
<item type="id" name="snapshot_view_binding_root" />
+ <item type="id" name="brightness_dialog_slider" />
+
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt
index 8639ee5..02161d2 100644
--- a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt
+++ b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt
@@ -20,21 +20,31 @@
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawWithCache
+import androidx.compose.ui.geometry.CornerRadius
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.compose.PlatformSlider
+import com.android.compose.ui.graphics.drawInOverlay
import com.android.systemui.Flags
import com.android.systemui.brightness.shared.model.GammaBrightness
import com.android.systemui.brightness.ui.viewmodel.BrightnessSliderViewModel
@@ -42,12 +52,13 @@
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.ui.compose.Icon
+import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.haptics.slider.SeekableSliderTrackerConfig
import com.android.systemui.haptics.slider.SliderHapticFeedbackConfig
import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.res.R
import com.android.systemui.utils.PolicyRestriction
-import com.android.app.tracing.coroutines.launchTraced as launch
@Composable
private fun BrightnessSlider(
@@ -104,7 +115,7 @@
}
},
modifier =
- modifier.clickable(enabled = isRestricted) {
+ modifier.sysuiResTag("slider").clickable(enabled = isRestricted) {
if (restriction is PolicyRestriction.Restricted) {
onRestrictedClick(restriction)
}
@@ -127,27 +138,55 @@
)
}
+private val sliderBackgroundFrameSize = 8.dp
+
+private fun Modifier.sliderBackground(color: Color) = drawWithCache {
+ val offsetAround = sliderBackgroundFrameSize.toPx()
+ val newSize = Size(size.width + 2 * offsetAround, size.height + 2 * offsetAround)
+ val offset = Offset(-offsetAround, -offsetAround)
+ val cornerRadius = CornerRadius(offsetAround + size.height / 2)
+ onDrawBehind {
+ drawRoundRect(color = color, topLeft = offset, size = newSize, cornerRadius = cornerRadius)
+ }
+}
+
@Composable
-fun BrightnessSliderContainer(viewModel: BrightnessSliderViewModel, modifier: Modifier = Modifier) {
- val state by viewModel.currentBrightness.collectAsStateWithLifecycle()
- val gamma = state.value
+fun BrightnessSliderContainer(
+ viewModel: BrightnessSliderViewModel,
+ modifier: Modifier = Modifier,
+ containerColor: Color = colorResource(R.color.shade_scrim_background_dark),
+) {
+ val gamma = viewModel.currentBrightness.value
val coroutineScope = rememberCoroutineScope()
val restriction by
viewModel.policyRestriction.collectAsStateWithLifecycle(
initialValue = PolicyRestriction.NoRestriction
)
- BrightnessSlider(
- gammaValue = gamma,
- valueRange = viewModel.minBrightness.value..viewModel.maxBrightness.value,
- label = viewModel.label,
- icon = viewModel.icon,
- restriction = restriction,
- onRestrictedClick = viewModel::showPolicyRestrictionDialog,
- onDrag = { coroutineScope.launch { viewModel.onDrag(Drag.Dragging(GammaBrightness(it))) } },
- onStop = { coroutineScope.launch { viewModel.onDrag(Drag.Stopped(GammaBrightness(it))) } },
- modifier = modifier.fillMaxWidth(),
- formatter = viewModel::formatValue,
- hapticsViewModelFactory = viewModel.hapticsViewModelFactory,
- )
+ DisposableEffect(Unit) { onDispose { viewModel.setIsDragging(false) } }
+
+ Box(modifier = modifier.fillMaxWidth().sysuiResTag("brightness_slider")) {
+ BrightnessSlider(
+ gammaValue = gamma,
+ valueRange = viewModel.minBrightness.value..viewModel.maxBrightness.value,
+ label = viewModel.label,
+ icon = viewModel.icon,
+ restriction = restriction,
+ onRestrictedClick = viewModel::showPolicyRestrictionDialog,
+ onDrag = {
+ viewModel.setIsDragging(true)
+ coroutineScope.launch { viewModel.onDrag(Drag.Dragging(GammaBrightness(it))) }
+ },
+ onStop = {
+ viewModel.setIsDragging(false)
+ coroutineScope.launch { viewModel.onDrag(Drag.Stopped(GammaBrightness(it))) }
+ },
+ modifier =
+ Modifier.then(if (viewModel.showMirror) Modifier.drawInOverlay() else Modifier)
+ .sliderBackground(containerColor)
+ .fillMaxWidth(),
+ formatter = viewModel::formatValue,
+ hapticsViewModelFactory = viewModel.hapticsViewModelFactory,
+ )
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt
index 074ac50..a61ce8f 100644
--- a/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt
@@ -16,36 +16,50 @@
package com.android.systemui.brightness.ui.viewmodel
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.setValue
import com.android.systemui.brightness.domain.interactor.BrightnessPolicyEnforcementInteractor
import com.android.systemui.brightness.domain.interactor.ScreenBrightnessInteractor
import com.android.systemui.brightness.shared.model.GammaBrightness
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.res.R
+import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor
import com.android.systemui.utils.PolicyRestriction
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.stateIn
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
-@SysUISingleton
+/**
+ * View Model for a brightness slider.
+ *
+ * If this brightness slider supports mirroring (show on top of current activity while dragging),
+ * then:
+ * * [showMirror] will be true while dragging
+ * * [BrightnessMirrorShowingInteractor.isShowing] will track if the mirror should show (for (other
+ * parts of SystemUI to act accordingly).
+ */
class BrightnessSliderViewModel
-@Inject
+@AssistedInject
constructor(
private val screenBrightnessInteractor: ScreenBrightnessInteractor,
private val brightnessPolicyEnforcementInteractor: BrightnessPolicyEnforcementInteractor,
- @Application private val applicationScope: CoroutineScope,
val hapticsViewModelFactory: SliderHapticsViewModel.Factory,
-) {
- val currentBrightness =
- screenBrightnessInteractor.gammaBrightness.stateIn(
- applicationScope,
- SharingStarted.WhileSubscribed(),
+ private val brightnessMirrorShowingInteractor: BrightnessMirrorShowingInteractor,
+ @Assisted private val supportsMirroring: Boolean,
+) : ExclusiveActivatable() {
+
+ private val hydrator = Hydrator("BrightnessSliderViewModel.hydrator")
+
+ val currentBrightness by
+ hydrator.hydratedStateOf(
+ "currentBrightness",
GammaBrightness(0),
+ screenBrightnessInteractor.gammaBrightness,
)
val maxBrightness = screenBrightnessInteractor.maxGammaBrightness
@@ -82,8 +96,26 @@
// This is not finalized UI so using fixed string
return "$percentage%"
}
+
+ fun setIsDragging(dragging: Boolean) {
+ brightnessMirrorShowingInteractor.setMirrorShowing(dragging && supportsMirroring)
+ }
+
+ val showMirror by
+ hydrator.hydratedStateOf("showMirror", brightnessMirrorShowingInteractor.isShowing)
+
+ override suspend fun onActivated(): Nothing {
+ hydrator.activate()
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(supportsMirroring: Boolean): BrightnessSliderViewModel
+ }
}
+fun BrightnessSliderViewModel.Factory.create() = create(supportsMirroring = true)
+
/** Represents a drag event in a brightness slider. */
sealed interface Drag {
val brightness: GammaBrightness
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
index e4738a2..9c8e84f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -59,6 +59,9 @@
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.input.pointer.PointerEventPass
+import androidx.compose.ui.input.pointer.PointerInputChange
+import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.approachLayout
import androidx.compose.ui.layout.onPlaced
import androidx.compose.ui.layout.onSizeChanged
@@ -76,6 +79,7 @@
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.compose.animation.scene.ContentKey
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.ElementMatcher
@@ -131,7 +135,6 @@
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.map
-import com.android.app.tracing.coroutines.launchTraced as launch
@SuppressLint("ValidFragment")
class QSFragmentCompose
@@ -149,11 +152,11 @@
private lateinit var viewModel: QSFragmentComposeViewModel
- private val qsHeight = MutableStateFlow(0)
private val qqsVisible = MutableStateFlow(false)
private val qqsPositionOnRoot = Rect()
private val composeViewPositionOnScreen = Rect()
private val scrollState = ScrollState(0)
+ private val locationTemp = IntArray(2)
// Inside object for namespacing
private val notificationScrimClippingParams =
@@ -247,7 +250,11 @@
Modifier.notificationScrimClip {
notificationScrimClippingParams.params
}
- },
+ }
+ // Disable touches in the whole composable while the mirror is showing.
+ // While the mirror is showing, an ancestor of the ComposeView is made
+ // alpha 0, but touches are still being captured by the composables.
+ .gesturesDisabled(viewModel.showingMirror),
) {
val isEditing by
viewModel.containerViewModel.editModeViewModel.isEditing
@@ -324,8 +331,27 @@
}
override fun getQsMinExpansionHeight(): Int {
- // TODO (b/353253277) implement split screen
- return viewModel.qqsHeight
+ return if (viewModel.isInSplitShade) {
+ getQsMinExpansionHeightForSplitShade()
+ } else {
+ viewModel.qqsHeight
+ }
+ }
+
+ /**
+ * Returns the min expansion height for split shade.
+ *
+ * On split shade, QS is always expanded and goes from the top of the screen to the bottom of
+ * the QS container.
+ */
+ private fun getQsMinExpansionHeightForSplitShade(): Int {
+ view?.getLocationOnScreen(locationTemp)
+ val top = locationTemp.get(1)
+ // We want to get the original top position, so we subtract any translation currently set.
+ val originalTop = (top - (view?.translationY ?: 0f)).toInt()
+ // On split shade the QS view doesn't start at the top of the screen, so we need to add the
+ // top margin.
+ return originalTop + (view?.height ?: 0)
}
override fun getDesiredHeight(): Int {
@@ -629,14 +655,15 @@
)
}
}
- }
- QuickSettingsTheme {
- FooterActions(
- viewModel = viewModel.footerActionsViewModel,
- qsVisibilityLifecycleOwner = this@QSFragmentCompose,
- modifier =
- Modifier.sysuiResTag("qs_footer_actions").element(ElementKeys.FooterActions),
- )
+ QuickSettingsTheme {
+ FooterActions(
+ viewModel = viewModel.footerActionsViewModel,
+ qsVisibilityLifecycleOwner = this@QSFragmentCompose,
+ modifier =
+ Modifier.sysuiResTag("qs_footer_actions")
+ .element(ElementKeys.FooterActions),
+ )
+ }
}
}
}
@@ -871,3 +898,19 @@
return super.onInterceptTouchEvent(ev)
}
}
+
+private fun Modifier.gesturesDisabled(disabled: Boolean) =
+ if (disabled) {
+ pointerInput(Unit) {
+ awaitPointerEventScope {
+ // we should wait for all new pointer events
+ while (true) {
+ awaitPointerEvent(pass = PointerEventPass.Initial)
+ .changes
+ .forEach(PointerInputChange::consume)
+ }
+ }
+ }
+ } else {
+ this
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
index d571dd0..d30c6be 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
@@ -26,6 +26,7 @@
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.lifecycle.LifecycleCoroutineScope
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.keyguard.BouncerPanelExpansionCalculator
import com.android.systemui.Dumpable
import com.android.systemui.animation.ShadeInterpolation
@@ -65,13 +66,12 @@
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
-import com.android.app.tracing.coroutines.launchTraced as launch
@OptIn(ExperimentalCoroutinesApi::class)
class QSFragmentComposeViewModel
@AssistedInject
constructor(
- val containerViewModel: QuickSettingsContainerViewModel,
+ containerViewModelFactory: QuickSettingsContainerViewModel.Factory,
@Main private val resources: Resources,
footerActionsViewModelFactory: FooterActionsViewModel.Factory,
private val footerActionsController: FooterActionsController,
@@ -87,6 +87,8 @@
@Assisted private val lifecycleScope: LifecycleCoroutineScope,
) : Dumpable, ExclusiveActivatable() {
+ val containerViewModel = containerViewModelFactory.create(true)
+
private val hydrator = Hydrator("QSFragmentComposeViewModel.hydrator")
val footerActionsViewModel =
@@ -257,6 +259,9 @@
.onStart { emit(sysuiStatusBarStateController.state) },
)
+ val showingMirror: Boolean
+ get() = containerViewModel.brightnessSliderViewModel.showMirror
+
private val isKeyguardState: Boolean
get() = statusBarState == StatusBarState.KEYGUARD
@@ -321,6 +326,7 @@
coroutineScope {
launch { hydrateSquishinessInteractor() }
launch { hydrator.activate() }
+ launch { containerViewModel.activate() }
awaitCancellation()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt
index 6ccd169..b1eb3bb3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt
@@ -17,18 +17,33 @@
package com.android.systemui.qs.ui.viewmodel
import com.android.systemui.brightness.ui.viewmodel.BrightnessSliderViewModel
-import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.qs.panels.ui.viewmodel.EditModeViewModel
import com.android.systemui.qs.panels.ui.viewmodel.QuickQuickSettingsViewModel
import com.android.systemui.qs.panels.ui.viewmodel.TileGridViewModel
-import javax.inject.Inject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
-@SysUISingleton
class QuickSettingsContainerViewModel
-@Inject
+@AssistedInject
constructor(
- val brightnessSliderViewModel: BrightnessSliderViewModel,
+ brightnessSliderViewModelFactory: BrightnessSliderViewModel.Factory,
+ @Assisted supportsBrightnessMirroring: Boolean,
val tileGridViewModel: TileGridViewModel,
val editModeViewModel: EditModeViewModel,
val quickQuickSettingsViewModel: QuickQuickSettingsViewModel,
-)
+) : ExclusiveActivatable() {
+
+ val brightnessSliderViewModel =
+ brightnessSliderViewModelFactory.create(supportsBrightnessMirroring)
+
+ override suspend fun onActivated(): Nothing {
+ brightnessSliderViewModel.activate()
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(supportsBrightnessMirroring: Boolean): QuickSettingsContainerViewModel
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
index 31519a9..9a416d1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
@@ -23,6 +23,7 @@
import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.UserActionResult.HideOverlay
import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay
+import com.android.systemui.qs.panels.ui.viewmodel.EditModeViewModel
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge
import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel
@@ -33,11 +34,10 @@
/** Models the UI state for the user actions for navigating to other scenes or overlays. */
class QuickSettingsShadeOverlayActionsViewModel
@AssistedInject
-constructor(private val containerViewModel: QuickSettingsContainerViewModel) :
- UserActionsViewModel() {
+constructor(private val editModeViewModel: EditModeViewModel) : UserActionsViewModel() {
override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
- containerViewModel.editModeViewModel.isEditing
+ editModeViewModel.isEditing
.map { isEditing ->
buildMap {
put(Swipe.Up, HideOverlay(Overlays.QuickSettingsShade))
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt
index bed8574..8ef51af 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.ui.viewmodel
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.Scenes
@@ -27,7 +28,6 @@
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
-import com.android.app.tracing.coroutines.launchTraced as launch
/**
* Models UI state used to render the content of the quick settings shade overlay.
@@ -41,9 +41,11 @@
val shadeInteractor: ShadeInteractor,
val sceneInteractor: SceneInteractor,
val shadeHeaderViewModelFactory: ShadeHeaderViewModel.Factory,
- val quickSettingsContainerViewModel: QuickSettingsContainerViewModel,
+ quickSettingsContainerViewModelFactory: QuickSettingsContainerViewModel.Factory,
) : ExclusiveActivatable() {
+ val quickSettingsContainerViewModel = quickSettingsContainerViewModelFactory.create(false)
+
override suspend fun onActivated(): Nothing {
coroutineScope {
launch {
@@ -68,6 +70,8 @@
)
}
}
+
+ launch { quickSettingsContainerViewModel.activate() }
}
awaitCancellation()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModel.kt
deleted file mode 100644
index d01b33b..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModel.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2024 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.qs.ui.viewmodel
-
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
-
-/**
- * Models UI state used to render the content of the quick settings shade scene.
- *
- * Different from [QuickSettingsShadeUserActionsViewModel], which only models user actions that can
- * be performed to navigate to other scenes.
- */
-class QuickSettingsShadeSceneContentViewModel
-@AssistedInject
-constructor(
- val quickSettingsContainerViewModel: QuickSettingsContainerViewModel,
-) {
-
- @AssistedFactory
- interface Factory {
- fun create(): QuickSettingsShadeSceneContentViewModel
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModel.kt
deleted file mode 100644
index bd1872d..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModel.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2024 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.qs.ui.viewmodel
-
-import com.android.compose.animation.scene.Back
-import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
-import com.android.compose.animation.scene.UserAction
-import com.android.compose.animation.scene.UserActionResult
-import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay
-import com.android.systemui.scene.shared.model.Overlays
-import com.android.systemui.scene.shared.model.SceneFamilies
-import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge
-import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
-import kotlinx.coroutines.flow.map
-
-/**
- * Models the UI state for the user actions that the user can perform to navigate to other scenes.
- *
- * Different from the [QuickSettingsShadeSceneContentViewModel] which models the _content_ of the
- * scene.
- */
-class QuickSettingsShadeUserActionsViewModel
-@AssistedInject
-constructor(
- val quickSettingsContainerViewModel: QuickSettingsContainerViewModel,
-) : UserActionsViewModel() {
-
- override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
- quickSettingsContainerViewModel.editModeViewModel.isEditing
- .map { editing ->
- buildMap {
- put(Swipe.Up, UserActionResult(SceneFamilies.Home))
- put(
- Swipe(
- direction = SwipeDirection.Down,
- fromSource = SceneContainerEdge.TopLeft
- ),
- ReplaceByOverlay(Overlays.NotificationsShade)
- )
- if (!editing) {
- put(Back, UserActionResult(SceneFamilies.Home))
- }
- }
- }
- .collect { actions -> setActions(actions) }
- }
-
- @AssistedFactory
- interface Factory {
- fun create(): QuickSettingsShadeUserActionsViewModel
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
index a62edcb..8c004c4 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
@@ -39,10 +39,16 @@
import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
+import androidx.annotation.NonNull;
+import androidx.compose.ui.platform.ComposeView;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.brightness.ui.viewmodel.BrightnessSliderViewModel;
+import com.android.systemui.compose.ComposeInitializer;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.qs.flags.QSComposeFragment;
import com.android.systemui.res.R;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
@@ -65,6 +71,7 @@
private final AccessibilityManagerWrapper mAccessibilityMgr;
private Runnable mCancelTimeoutRunnable;
private final ShadeInteractor mShadeInteractor;
+ private final BrightnessSliderViewModel.Factory mBrightnessSliderViewModelFactory;
@Inject
public BrightnessDialog(
@@ -72,13 +79,15 @@
BrightnessController.Factory brightnessControllerFactory,
@Main DelayableExecutor mainExecutor,
AccessibilityManagerWrapper accessibilityMgr,
- ShadeInteractor shadeInteractor
+ ShadeInteractor shadeInteractor,
+ BrightnessSliderViewModel.Factory brightnessSliderViewModelFactory
) {
mToggleSliderFactory = brightnessSliderfactory;
mBrightnessControllerFactory = brightnessControllerFactory;
mMainExecutor = mainExecutor;
mAccessibilityMgr = accessibilityMgr;
mShadeInteractor = shadeInteractor;
+ mBrightnessSliderViewModelFactory = brightnessSliderViewModelFactory;
}
@@ -86,14 +95,28 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setWindowAttributes();
- setContentView(R.layout.brightness_mirror_container);
- setBrightnessDialogViewAttributes();
+ View view;
+ if (!QSComposeFragment.isEnabled()) {
+ setContentView(R.layout.brightness_mirror_container);
+ view = findViewById(R.id.brightness_mirror_container);
+ setDialogContent((FrameLayout) view);
+ } else {
+ ComposeView composeView = new ComposeView(this);
+ ComposeDialogComposableProvider.INSTANCE.setComposableBrightness(
+ composeView,
+ new ComposableProvider(mBrightnessSliderViewModelFactory)
+ );
+ composeView.setId(R.id.brightness_dialog_slider);
+ setContentView(composeView);
+ ((ViewGroup) composeView.getParent()).setClipChildren(false);
+ view = composeView;
+ }
+ setBrightnessDialogViewAttributes(view);
if (mShadeInteractor.isQsExpanded().getValue()) {
finish();
}
- View view = findViewById(R.id.brightness_mirror_container);
if (view != null) {
collectFlow(view, mShadeInteractor.isQsExpanded(), this::onShadeStateChange);
}
@@ -117,13 +140,27 @@
window.getDecorView();
window.setLayout(WRAP_CONTENT, WRAP_CONTENT);
getTheme().applyStyle(R.style.Theme_SystemUI_QuickSettings, false);
+ if (QSComposeFragment.isEnabled()) {
+ window.getDecorView().addOnAttachStateChangeListener(
+ new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(@NonNull View v) {
+ ComposeInitializer.INSTANCE.onAttachedToWindow(v);
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(@NonNull View v) {
+ ComposeInitializer.INSTANCE.onDetachedFromWindow(v);
+ }
+ });
+ }
}
- void setBrightnessDialogViewAttributes() {
- FrameLayout frame = findViewById(R.id.brightness_mirror_container);
+ void setBrightnessDialogViewAttributes(View container) {
// The brightness mirror container is INVISIBLE by default.
- frame.setVisibility(View.VISIBLE);
- ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) frame.getLayoutParams();
+ container.setVisibility(View.VISIBLE);
+ ViewGroup.MarginLayoutParams lp =
+ (ViewGroup.MarginLayoutParams) container.getLayoutParams();
int horizontalMargin =
getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
lp.leftMargin = horizontalMargin;
@@ -136,23 +173,6 @@
lp.topMargin = verticalMargin;
lp.bottomMargin = verticalMargin;
- frame.setLayoutParams(lp);
- Rect bounds = new Rect();
- frame.addOnLayoutChangeListener(
- (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
- // Exclude this view (and its horizontal margins) from triggering gestures.
- // This prevents back gesture from being triggered by dragging close to the
- // edge of the slider (0% or 100%).
- bounds.set(-horizontalMargin, 0, right - left + horizontalMargin, bottom - top);
- v.setSystemGestureExclusionRects(List.of(bounds));
- });
-
- BrightnessSliderController controller = mToggleSliderFactory.create(this, frame);
- controller.init();
- frame.addView(controller.getRootView(), MATCH_PARENT, WRAP_CONTENT);
-
- mBrightnessController = mBrightnessControllerFactory.create(controller);
-
Configuration configuration = getResources().getConfiguration();
int orientation = configuration.orientation;
int windowWidth = getWindowAvailableWidth();
@@ -165,7 +185,23 @@
lp.width = windowWidth - horizontalMargin * 2;
}
- frame.setLayoutParams(lp);
+ container.setLayoutParams(lp);
+ Rect bounds = new Rect();
+ container.addOnLayoutChangeListener(
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+ // Exclude this view (and its horizontal margins) from triggering gestures.
+ // This prevents back gesture from being triggered by dragging close to the
+ // edge of the slider (0% or 100%).
+ bounds.set(-horizontalMargin, 0, right - left + horizontalMargin, bottom - top);
+ v.setSystemGestureExclusionRects(List.of(bounds));
+ });
+ }
+
+ private void setDialogContent(FrameLayout frame) {
+ BrightnessSliderController controller = mToggleSliderFactory.create(this, frame);
+ controller.init();
+ frame.addView(controller.getRootView(), MATCH_PARENT, WRAP_CONTENT);
+ mBrightnessController = mBrightnessControllerFactory.create(controller);
}
private int getWindowAvailableWidth() {
@@ -181,7 +217,9 @@
@Override
protected void onStart() {
super.onStart();
- mBrightnessController.registerCallbacks();
+ if (!QSComposeFragment.isEnabled()) {
+ mBrightnessController.registerCallbacks();
+ }
MetricsLogger.visible(this, MetricsEvent.BRIGHTNESS_DIALOG);
}
@@ -203,7 +241,9 @@
protected void onStop() {
super.onStop();
MetricsLogger.hidden(this, MetricsEvent.BRIGHTNESS_DIALOG);
- mBrightnessController.unregisterCallbacks();
+ if (!QSComposeFragment.isEnabled()) {
+ mBrightnessController.unregisterCallbacks();
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/ComposeDialogComposableProvider.kt b/packages/SystemUI/src/com/android/systemui/settings/brightness/ComposeDialogComposableProvider.kt
new file mode 100644
index 0000000..dde2ebc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ComposeDialogComposableProvider.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2024 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.settings.brightness
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.ViewCompositionStrategy
+import com.android.compose.theme.PlatformTheme
+import com.android.systemui.brightness.ui.compose.BrightnessSliderContainer
+import com.android.systemui.brightness.ui.viewmodel.BrightnessSliderViewModel
+import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.qs.ui.composable.QuickSettingsShade
+
+object ComposeDialogComposableProvider {
+
+ fun setComposableBrightness(composeView: ComposeView, content: ComposableProvider) {
+ composeView.apply {
+ setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
+ setContent { PlatformTheme { content.ProvideComposableContent() } }
+ }
+ }
+}
+
+@Composable
+private fun BrightnessSliderForDialog(
+ brightnessSliderViewModelFactory: BrightnessSliderViewModel.Factory
+) {
+ val viewModel =
+ rememberViewModel(traceName = "BrightnessDialog.viewModel") {
+ brightnessSliderViewModelFactory.create(false)
+ }
+ BrightnessSliderContainer(
+ viewModel = viewModel,
+ Modifier.fillMaxWidth().height(QuickSettingsShade.Dimensions.BrightnessSliderHeight),
+ )
+}
+
+class ComposableProvider(
+ private val brightnessSliderViewModelFactory: BrightnessSliderViewModel.Factory
+) {
+ @Composable
+ fun ProvideComposableContent() {
+ BrightnessSliderForDialog(brightnessSliderViewModelFactory)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/domain/interactor/BrightnessMirrorShowingInteractor.kt b/packages/SystemUI/src/com/android/systemui/settings/brightness/domain/interactor/BrightnessMirrorShowingInteractor.kt
index ef6e72f..de068a0 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/domain/interactor/BrightnessMirrorShowingInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/domain/interactor/BrightnessMirrorShowingInteractor.kt
@@ -23,9 +23,12 @@
@SysUISingleton
class BrightnessMirrorShowingInteractor
@Inject
-constructor(
- private val brightnessMirrorShowingRepository: BrightnessMirrorShowingRepository,
-) {
+constructor(private val brightnessMirrorShowingRepository: BrightnessMirrorShowingRepository) {
+ /**
+ * Whether a brightness mirror is showing (either as a compose overlay or as a separate mirror).
+ *
+ * This can be used to determine whether other views/composables have to be hidden.
+ */
val isShowing = brightnessMirrorShowingRepository.isShowing
fun setMirrorShowing(showing: Boolean) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 083cf1f..1b281b1 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -164,9 +164,11 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.power.shared.model.WakefulnessModel;
+import com.android.systemui.qs.flags.QSComposeFragment;
import com.android.systemui.res.R;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.scene.shared.model.Scenes;
+import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor;
import com.android.systemui.shade.data.repository.FlingInfo;
import com.android.systemui.shade.data.repository.ShadeRepository;
import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor;
@@ -664,6 +666,7 @@
};
private final ActivityStarter mActivityStarter;
+ private final BrightnessMirrorShowingInteractor mBrightnessMirrorShowingInteractor;
@Inject
public NotificationPanelViewController(NotificationPanelView view,
@@ -757,7 +760,8 @@
PowerInteractor powerInteractor,
KeyguardClockPositionAlgorithm keyguardClockPositionAlgorithm,
NaturalScrollingSettingObserver naturalScrollingSettingObserver,
- MSDLPlayer msdlPlayer) {
+ MSDLPlayer msdlPlayer,
+ BrightnessMirrorShowingInteractor brightnessMirrorShowingInteractor) {
SceneContainerFlag.assertInLegacyMode();
keyguardStateController.addCallback(new KeyguardStateController.Callback() {
@Override
@@ -944,6 +948,7 @@
},
mFalsingManager);
mActivityStarter = activityStarter;
+ mBrightnessMirrorShowingInteractor = brightnessMirrorShowingInteractor;
onFinishInflate();
keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(
new KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener() {
@@ -1185,6 +1190,12 @@
}
},
mMainDispatcher);
+ if (QSComposeFragment.isEnabled()) {
+ collectFlow(mView,
+ mBrightnessMirrorShowingInteractor.isShowing(),
+ isShowing -> setAlpha(isShowing ? 0 : 255, true)
+ );
+ }
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 365666d..be2bf82 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -54,8 +54,10 @@
import com.android.systemui.keyguard.shared.model.KeyguardState;
import com.android.systemui.keyguard.shared.model.TransitionState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
+import com.android.systemui.qs.flags.QSComposeFragment;
import com.android.systemui.res.R;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
+import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor;
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround;
import com.android.systemui.shared.animation.DisableSubpixelTextTransitionListener;
@@ -191,7 +193,8 @@
PrimaryBouncerInteractor primaryBouncerInteractor,
AlternateBouncerInteractor alternateBouncerInteractor,
BouncerViewBinder bouncerViewBinder,
- @ShadeDisplayAware ConfigurationForwarder configurationForwarder) {
+ @ShadeDisplayAware ConfigurationForwarder configurationForwarder,
+ BrightnessMirrorShowingInteractor brightnessMirrorShowingInteractor) {
mLockscreenShadeTransitionController = transitionController;
mFalsingCollector = falsingCollector;
mStatusBarStateController = statusBarStateController;
@@ -232,6 +235,11 @@
mView,
notificationLaunchAnimationInteractor.isLaunchAnimationRunning(),
this::setExpandAnimationRunning);
+ if (QSComposeFragment.isEnabled()) {
+ collectFlow(mView,
+ brightnessMirrorShowingInteractor.isShowing(),
+ this::setBrightnessMirrorShowingForDepth);
+ }
var keyguardUnfoldTransition = unfoldComponent.map(
SysUIUnfoldComponent::getKeyguardUnfoldTransition);
@@ -703,6 +711,10 @@
}
}
+ private void setBrightnessMirrorShowingForDepth(boolean showing) {
+ mDepthController.setBrightnessMirrorVisible(showing);
+ }
+
@Override
public void dump(PrintWriter pw, String[] args) {
pw.print(" mExpandingBelowNotch=");
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
index 437d32d..7a18d7c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
@@ -27,6 +27,7 @@
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
import androidx.lifecycle.lifecycleScope
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.fragments.FragmentService
@@ -49,7 +50,6 @@
import java.util.function.Consumer
import javax.inject.Inject
import kotlin.reflect.KMutableProperty0
-import com.android.app.tracing.coroutines.launchTraced as launch
@VisibleForTesting internal const val INSET_DEBOUNCE_MILLIS = 500L
@@ -334,7 +334,7 @@
private data class Paddings(
val containerPadding: Int,
val notificationsMargin: Int,
- val qsContainerPadding: Int
+ val qsContainerPadding: Int,
)
private fun KMutableProperty0<Int>.setAndReportChange(newValue: Int): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 65663fd..99efba4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -1105,7 +1105,7 @@
mJavaAdapter.alwaysCollectFlow(
mCommunalInteractor.isIdleOnCommunal(),
mIdleOnCommunalConsumer);
- if (SceneContainerFlag.isEnabled()) {
+ if (SceneContainerFlag.isEnabled() || QSComposeFragment.isEnabled()) {
mJavaAdapter.alwaysCollectFlow(
mBrightnessMirrorShowingInteractor.isShowing(),
this::setBrightnessMirrorShowing
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
similarity index 81%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
index a06784e..f7059e2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
@@ -18,25 +18,30 @@
import android.content.Intent
import android.graphics.Rect
+import android.platform.test.flag.junit.FlagsParameterization
import android.testing.TestableLooper
import android.view.View
import android.view.ViewGroup
import android.view.WindowManagerPolicyConstants.EXTRA_FROM_BRIGHTNESS_KEY
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.FlakyTest
import androidx.test.filters.SmallTest
import androidx.test.rule.ActivityTestRule
import com.android.systemui.SysuiTestCase
import com.android.systemui.activity.SingleActivityFactory
+import com.android.systemui.brightness.ui.viewmodel.BrightnessSliderViewModel
+import com.android.systemui.brightness.ui.viewmodel.brightnessSliderViewModelFactory
+import com.android.systemui.qs.flags.QSComposeFragment
import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
+import com.android.systemui.testKosmos
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.CountDownLatch
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.MutableStateFlow
@@ -53,11 +58,25 @@
import org.mockito.Mockito.eq
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
@SmallTest
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@TestableLooper.RunWithLooper
-class BrightnessDialogTest : SysuiTestCase() {
+class BrightnessDialogTest(val flags: FlagsParameterization) : SysuiTestCase() {
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
+ val viewId by lazy {
+ if (QSComposeFragment.isEnabled) {
+ R.id.brightness_dialog_slider
+ } else {
+ R.id.brightness_mirror_container
+ }
+ }
@Mock private lateinit var brightnessSliderControllerFactory: BrightnessSliderController.Factory
@Mock private lateinit var brightnessSliderController: BrightnessSliderController
@@ -66,10 +85,14 @@
@Mock private lateinit var accessibilityMgr: AccessibilityManagerWrapper
@Mock private lateinit var shadeInteractor: ShadeInteractor
+ private val kosmos = testKosmos()
+
private val clock = FakeSystemClock()
private val mainExecutor = FakeExecutor(clock)
- @Rule
+ private val onDestroyLatch = CountDownLatch(1)
+
+ @Rule(order = 200)
@JvmField
var activityRule =
ActivityTestRule(
@@ -79,7 +102,9 @@
brightnessControllerFactory,
mainExecutor,
accessibilityMgr,
- shadeInteractor
+ shadeInteractor,
+ kosmos.brightnessSliderViewModelFactory,
+ onDestroyLatch,
)
},
/* initialTouchMode= */ false,
@@ -99,12 +124,13 @@
@After
fun tearDown() {
activityRule.finishActivity()
+ onDestroyLatch.await()
}
@Test
fun testGestureExclusion() {
activityRule.launchActivity(Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG))
- val frame = activityRule.activity.requireViewById<View>(R.id.brightness_mirror_container)
+ val frame = activityRule.activity.requireViewById<View>(viewId)
val lp = frame.layoutParams as ViewGroup.MarginLayoutParams
val horizontalMargin =
@@ -125,7 +151,7 @@
`when`(
accessibilityMgr.getRecommendedTimeoutMillis(
eq(BrightnessDialog.DIALOG_TIMEOUT_MILLIS),
- anyInt()
+ anyInt(),
)
)
.thenReturn(BrightnessDialog.DIALOG_TIMEOUT_MILLIS)
@@ -144,7 +170,7 @@
`when`(
accessibilityMgr.getRecommendedTimeoutMillis(
eq(BrightnessDialog.DIALOG_TIMEOUT_MILLIS),
- anyInt()
+ anyInt(),
)
)
.thenReturn(BrightnessDialog.DIALOG_TIMEOUT_MILLIS)
@@ -171,7 +197,7 @@
`when`(
accessibilityMgr.getRecommendedTimeoutMillis(
eq(BrightnessDialog.DIALOG_TIMEOUT_MILLIS),
- anyInt()
+ anyInt(),
)
)
.thenReturn(BrightnessDialog.DIALOG_TIMEOUT_MILLIS)
@@ -205,14 +231,17 @@
brightnessControllerFactory: BrightnessController.Factory,
mainExecutor: DelayableExecutor,
accessibilityMgr: AccessibilityManagerWrapper,
- shadeInteractor: ShadeInteractor
+ shadeInteractor: ShadeInteractor,
+ brightnessSliderViewModelFactory: BrightnessSliderViewModel.Factory,
+ private val countdownLatch: CountDownLatch,
) :
BrightnessDialog(
brightnessSliderControllerFactory,
brightnessControllerFactory,
mainExecutor,
accessibilityMgr,
- shadeInteractor
+ shadeInteractor,
+ brightnessSliderViewModelFactory,
) {
var finishing = MutableStateFlow(false)
@@ -223,5 +252,18 @@
override fun requestFinish() {
finishing.value = true
}
+
+ override fun onDestroy() {
+ super.onDestroy()
+ countdownLatch.countDown()
+ }
+ }
+
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return FlagsParameterization.allCombinationsOf(QSComposeFragment.FLAG_NAME)
+ }
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt
index 6889b8a..52cdbed 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt
@@ -20,14 +20,19 @@
import com.android.systemui.brightness.domain.interactor.screenBrightnessInteractor
import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.settings.brightness.domain.interactor.brightnessMirrorShowingInteractor
-val Kosmos.brightnessSliderViewModel: BrightnessSliderViewModel by
+val Kosmos.brightnessSliderViewModelFactory: BrightnessSliderViewModel.Factory by
Kosmos.Fixture {
- BrightnessSliderViewModel(
- screenBrightnessInteractor = screenBrightnessInteractor,
- brightnessPolicyEnforcementInteractor = brightnessPolicyEnforcementInteractor,
- applicationScope = applicationCoroutineScope,
- hapticsViewModelFactory = sliderHapticsViewModelFactory,
- )
+ object : BrightnessSliderViewModel.Factory {
+ override fun create(allowsMirroring: Boolean): BrightnessSliderViewModel {
+ return BrightnessSliderViewModel(
+ screenBrightnessInteractor = screenBrightnessInteractor,
+ brightnessPolicyEnforcementInteractor = brightnessPolicyEnforcementInteractor,
+ hapticsViewModelFactory = sliderHapticsViewModelFactory,
+ brightnessMirrorShowingInteractor = brightnessMirrorShowingInteractor,
+ supportsMirroring = allowsMirroring,
+ )
+ }
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt
index dff5625..462b408 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt
@@ -26,7 +26,7 @@
import com.android.systemui.qs.footerActionsViewModelFactory
import com.android.systemui.qs.panels.domain.interactor.tileSquishinessInteractor
import com.android.systemui.qs.panels.ui.viewmodel.paginatedGridViewModel
-import com.android.systemui.qs.ui.viewmodel.quickSettingsContainerViewModel
+import com.android.systemui.qs.ui.viewmodel.quickSettingsContainerViewModelFactory
import com.android.systemui.shade.largeScreenHeaderHelper
import com.android.systemui.shade.transition.largeScreenShadeInterpolator
import com.android.systemui.statusbar.disableflags.data.repository.disableFlagsRepository
@@ -41,7 +41,7 @@
lifecycleScope: LifecycleCoroutineScope
): QSFragmentComposeViewModel {
return QSFragmentComposeViewModel(
- quickSettingsContainerViewModel,
+ quickSettingsContainerViewModelFactory,
mainResources,
footerActionsViewModelFactory,
footerActionsController,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelKosmos.kt
index 779634d..6ded751 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelKosmos.kt
@@ -16,18 +16,25 @@
package com.android.systemui.qs.ui.viewmodel
-import com.android.systemui.brightness.ui.viewmodel.brightnessSliderViewModel
+import com.android.systemui.brightness.ui.viewmodel.brightnessSliderViewModelFactory
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.panels.ui.viewmodel.editModeViewModel
import com.android.systemui.qs.panels.ui.viewmodel.quickQuickSettingsViewModel
import com.android.systemui.qs.panels.ui.viewmodel.tileGridViewModel
-val Kosmos.quickSettingsContainerViewModel by
+val Kosmos.quickSettingsContainerViewModelFactory by
Kosmos.Fixture {
- QuickSettingsContainerViewModel(
- brightnessSliderViewModel,
- tileGridViewModel,
- editModeViewModel,
- quickQuickSettingsViewModel,
- )
+ object : QuickSettingsContainerViewModel.Factory {
+ override fun create(
+ supportsBrightnessMirroring: Boolean
+ ): QuickSettingsContainerViewModel {
+ return QuickSettingsContainerViewModel(
+ brightnessSliderViewModelFactory,
+ supportsBrightnessMirroring,
+ tileGridViewModel,
+ editModeViewModel,
+ quickQuickSettingsViewModel,
+ )
+ }
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelKosmos.kt
index 6ced8c3..a1dbeaa 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelKosmos.kt
@@ -18,6 +18,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.qs.panels.ui.viewmodel.editModeViewModel
import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
import com.android.systemui.util.mockito.mock
@@ -25,5 +26,5 @@
val Kosmos.quickSettingsShadeOverlayActionsViewModel:
QuickSettingsShadeOverlayActionsViewModel by Fixture {
- QuickSettingsShadeOverlayActionsViewModel(quickSettingsContainerViewModel)
+ QuickSettingsShadeOverlayActionsViewModel(editModeViewModel)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelKosmos.kt
index 6540ed6..0462848 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelKosmos.kt
@@ -27,6 +27,6 @@
shadeInteractor = shadeInteractor,
sceneInteractor = sceneInteractor,
shadeHeaderViewModelFactory = shadeHeaderViewModelFactory,
- quickSettingsContainerViewModel = quickSettingsContainerViewModel,
+ quickSettingsContainerViewModelFactory = quickSettingsContainerViewModelFactory,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModelKosmos.kt
deleted file mode 100644
index cd1704c..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModelKosmos.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2024 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.qs.ui.viewmodel
-
-import com.android.systemui.kosmos.Kosmos
-
-val Kosmos.quickSettingsShadeSceneContentViewModel: QuickSettingsShadeSceneContentViewModel by
- Kosmos.Fixture {
- QuickSettingsShadeSceneContentViewModel(
- quickSettingsContainerViewModel = quickSettingsContainerViewModel,
- )
- }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelKosmos.kt
deleted file mode 100644
index 06592b1..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelKosmos.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2024 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.qs.ui.viewmodel
-
-import com.android.systemui.kosmos.Kosmos
-
-val Kosmos.quickSettingsShadeUserActionsViewModel: QuickSettingsShadeUserActionsViewModel by
- Kosmos.Fixture {
- QuickSettingsShadeUserActionsViewModel(
- quickSettingsContainerViewModel = quickSettingsContainerViewModel,
- )
- }