summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/window/domain/interactor/WindowRootViewBlurInteractorTest.kt13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModelTest.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/window/domain/interactor/WindowRootViewBlurInteractor.kt56
-rw-r--r--packages/SystemUI/src/com/android/systemui/window/ui/WindowRootViewBinder.kt64
-rw-r--r--packages/SystemUI/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModel.kt84
5 files changed, 103 insertions, 120 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/window/domain/interactor/WindowRootViewBlurInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/window/domain/interactor/WindowRootViewBlurInteractorTest.kt
index 79edc223bc83..9ca2aea4e963 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/window/domain/interactor/WindowRootViewBlurInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/window/domain/interactor/WindowRootViewBlurInteractorTest.kt
@@ -20,7 +20,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
-import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -37,18 +36,6 @@ class WindowRootViewBlurInteractorTest : SysuiTestCase() {
val underTest by lazy { kosmos.windowRootViewBlurInteractor }
@Test
- fun bouncerBlurIsAppliedImmediately() =
- testScope.runTest {
- val blurRadius by collectLastValue(underTest.blurRadius)
- val isBlurOpaque by collectLastValue(underTest.isBlurOpaque)
-
- underTest.requestBlurForBouncer(10)
-
- assertThat(blurRadius).isEqualTo(10)
- assertThat(isBlurOpaque).isFalse()
- }
-
- @Test
fun shadeBlurIsNotAppliedWhenBouncerBlurIsActive() =
testScope.runTest {
kosmos.fakeKeyguardBouncerRepository.setPrimaryShow(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModelTest.kt
index 3c4e3612c053..61ee5e04afd9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModelTest.kt
@@ -46,12 +46,14 @@ class WindowRootViewModelTest : SysuiTestCase() {
@Test
fun bouncerTransitionChangesWindowBlurRadius() =
testScope.runTest {
- val blurState by collectLastValue(underTest.blurState)
+ val blurRadius by collectLastValue(underTest.blurRadius)
+ val isBlurOpaque by collectLastValue(underTest.isBlurOpaque)
runCurrent()
kosmos.fakeBouncerTransitions.first().windowBlurRadius.value = 30.0f
runCurrent()
- assertThat(blurState).isEqualTo(BlurState(radius = 30, isOpaque = false))
+ assertThat(blurRadius).isEqualTo(30)
+ assertThat(isBlurOpaque).isEqualTo(false)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/window/domain/interactor/WindowRootViewBlurInteractor.kt b/packages/SystemUI/src/com/android/systemui/window/domain/interactor/WindowRootViewBlurInteractor.kt
index bfa349876c42..e21e0a1cadc7 100644
--- a/packages/SystemUI/src/com/android/systemui/window/domain/interactor/WindowRootViewBlurInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/window/domain/interactor/WindowRootViewBlurInteractor.kt
@@ -32,7 +32,9 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@@ -72,39 +74,29 @@ constructor(
/** Radius of blur to be applied on the window root view. */
val blurRadius: StateFlow<Int> = repository.blurRadius.asStateFlow()
- /** Whether the blur applied is opaque or transparent. */
- val isBlurOpaque: StateFlow<Boolean> = repository.isBlurOpaque.asStateFlow()
-
/**
* Emits the applied blur radius whenever blur is successfully applied to the window root view.
*/
val onBlurAppliedEvent: Flow<Int> = repository.onBlurApplied
- /**
- * Request to apply blur while on bouncer, this takes precedence over other blurs (from shade).
- */
- fun requestBlurForBouncer(blurRadius: Int) {
- repository.isBlurOpaque.value = false
- repository.blurRadius.value = blurRadius
- }
-
- /**
- * Request to apply blur while on glanceable hub, this takes precedence over other blurs (from
- * shade) except for bouncer.
- */
- fun requestBlurForGlanceableHub(blurRadius: Int): Boolean {
- if (keyguardInteractor.primaryBouncerShowing.value) {
- return false
+ /** Whether the blur applied is opaque or transparent. */
+ val isBlurOpaque: Flow<Boolean> =
+ combine(
+ if (Flags.bouncerUiRevamp()) {
+ keyguardInteractor.primaryBouncerShowing.or(isBouncerTransitionInProgress)
+ } else {
+ flowOf(false)
+ },
+ if (Flags.glanceableHubBlurredBackground()) {
+ communalInteractor.isCommunalBlurring
+ } else {
+ flowOf(false)
+ },
+ repository.isBlurOpaque,
+ ) { bouncerActive, ghActive, shadeBlurOpaque ->
+ if (bouncerActive || ghActive) false else shadeBlurOpaque
}
- Log.d(TAG, "requestBlurForGlanceableHub for $blurRadius")
-
- repository.isBlurOpaque.value = false
- repository.blurRadius.value = blurRadius
-
- return true
- }
-
/**
* Method that requests blur to be applied on window root view. It is applied only when other
* blurs are not applied.
@@ -119,10 +111,10 @@ constructor(
// We need to check either of these because they are two different sources of truth,
// primaryBouncerShowing changes early to true/false, but blur is
// coordinated by transition value.
- if (keyguardInteractor.primaryBouncerShowing.value || isBouncerTransitionInProgress.value) {
+ if (isBouncerTransitionInProgress()) {
return false
}
- if (communalInteractor.isCommunalBlurring.value) {
+ if (isGlanceableHubActive()) {
return false
}
Log.d(TAG, "requestingBlurForShade for $blurRadius $opaque")
@@ -131,6 +123,14 @@ constructor(
return true
}
+ private fun isGlanceableHubActive() = communalInteractor.isCommunalBlurring.value
+
+ private fun isBouncerTransitionInProgress() =
+ keyguardInteractor.primaryBouncerShowing.value || isBouncerTransitionInProgress.value
+
+ private fun Flow<Boolean>.or(anotherFlow: Flow<Boolean>): Flow<Boolean> =
+ this.combine(anotherFlow) { a, b -> a || b }
+
companion object {
const val TAG = "WindowRootViewBlurInteractor"
}
diff --git a/packages/SystemUI/src/com/android/systemui/window/ui/WindowRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/window/ui/WindowRootViewBinder.kt
index bba0c378e2a5..153df7f29737 100644
--- a/packages/SystemUI/src/com/android/systemui/window/ui/WindowRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/window/ui/WindowRootViewBinder.kt
@@ -20,6 +20,7 @@ import android.util.Log
import android.view.Choreographer
import android.view.Choreographer.FrameCallback
import com.android.app.tracing.coroutines.TrackTracer
+import com.android.app.tracing.coroutines.launchTraced
import com.android.systemui.Flags
import com.android.systemui.lifecycle.WindowLifecycleState
import com.android.systemui.lifecycle.repeatWhenAttached
@@ -29,8 +30,8 @@ import com.android.systemui.statusbar.BlurUtils
import com.android.systemui.window.ui.viewmodel.WindowRootViewModel
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.launch
/**
* View binder that wires up window level UI transformations like blur to the [WindowRootView]
@@ -51,7 +52,6 @@ object WindowRootViewBinder {
view.repeatWhenAttached(mainDispatcher) {
Log.d(TAG, "Binding root view")
- var frameCallbackPendingExecution: FrameCallback? = null
view.viewModel(
minWindowLifecycleState = WindowLifecycleState.ATTACHED,
factory = { viewModelFactory.create() },
@@ -59,34 +59,48 @@ object WindowRootViewBinder {
) { viewModel ->
try {
Log.d(TAG, "Launching coroutines that update window root view state")
- launch {
- viewModel.blurState
- .filter { it.radius >= 0 }
- .collect { blurState ->
- val newFrameCallback = FrameCallback {
- frameCallbackPendingExecution = null
- blurUtils.applyBlur(
- view.rootView?.viewRootImpl,
- blurState.radius,
- blurState.isOpaque,
- )
- TrackTracer.instantForGroup(
- "windowBlur",
- "appliedBlurRadius",
- blurState.radius,
- )
- viewModel.onBlurApplied(blurState.radius)
+ launchTraced("WindowBlur") {
+ var wasUpdateScheduledForThisFrame = false
+ var lastScheduledBlurRadius = 0
+ var lastScheduleBlurOpaqueness = false
+
+ // Creating the callback once and not for every coroutine invocation
+ val newFrameCallback = FrameCallback {
+ wasUpdateScheduledForThisFrame = false
+ val blurRadiusToApply = lastScheduledBlurRadius
+ blurUtils.applyBlur(
+ view.rootView?.viewRootImpl,
+ blurRadiusToApply,
+ lastScheduleBlurOpaqueness,
+ )
+ TrackTracer.instantForGroup(
+ "windowBlur",
+ "appliedBlurRadius",
+ blurRadiusToApply,
+ )
+ viewModel.onBlurApplied(blurRadiusToApply)
+ }
+
+ combine(viewModel.blurRadius, viewModel.isBlurOpaque, ::Pair)
+ .filter { it.first >= 0 }
+ .collect { (blurRadius, isOpaque) ->
+ // Expectation is that we schedule only one blur radius value
+ // per frame
+ if (wasUpdateScheduledForThisFrame) {
+ return@collect
}
TrackTracer.instantForGroup(
"windowBlur",
"preparedBlurRadius",
- blurState.radius,
+ blurRadius,
+ )
+ lastScheduledBlurRadius = blurRadius.toInt()
+ lastScheduleBlurOpaqueness = isOpaque
+ wasUpdateScheduledForThisFrame = true
+ blurUtils.prepareBlur(
+ view.rootView?.viewRootImpl,
+ lastScheduledBlurRadius,
)
- blurUtils.prepareBlur(view.rootView?.viewRootImpl, blurState.radius)
- if (frameCallbackPendingExecution != null) {
- choreographer.removeFrameCallback(frameCallbackPendingExecution)
- }
- frameCallbackPendingExecution = newFrameCallback
choreographer.postFrameCallback(newFrameCallback)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModel.kt
index 72cca75df92c..b18c39dcf5e0 100644
--- a/packages/SystemUI/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModel.kt
@@ -19,7 +19,7 @@ package com.android.systemui.window.ui.viewmodel
import android.os.Build
import android.util.Log
import com.android.app.tracing.coroutines.launchTraced
-import com.android.systemui.Flags.glanceableHubBlurredBackground
+import com.android.systemui.Flags
import com.android.systemui.keyguard.ui.transitions.GlanceableHubTransition
import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
import com.android.systemui.lifecycle.ExclusiveActivatable
@@ -29,9 +29,9 @@ import dagger.assisted.AssistedInject
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach
@@ -41,14 +41,33 @@ typealias BlurAppliedUiEvent = Int
class WindowRootViewModel
@AssistedInject
constructor(
- private val primaryBouncerTransitions: Set<@JvmSuppressWildcards PrimaryBouncerTransition>,
- private val glanceableHubTransitions: Set<@JvmSuppressWildcards GlanceableHubTransition>,
+ primaryBouncerTransitions: Set<@JvmSuppressWildcards PrimaryBouncerTransition>,
+ glanceableHubTransitions: Set<@JvmSuppressWildcards GlanceableHubTransition>,
private val blurInteractor: WindowRootViewBlurInteractor,
) : ExclusiveActivatable() {
private val blurEvents = Channel<BlurAppliedUiEvent>(Channel.BUFFERED)
- private val _blurState = MutableStateFlow(BlurState(0, false))
- val blurState = _blurState.asStateFlow()
+
+ private val bouncerBlurRadiusFlows =
+ if (Flags.bouncerUiRevamp())
+ primaryBouncerTransitions.map { it.windowBlurRadius.logIfPossible(it.javaClass.name) }
+ else emptyList()
+
+ private val glanceableHubBlurRadiusFlows =
+ if (Flags.glanceableHubBlurredBackground())
+ glanceableHubTransitions.map { it.windowBlurRadius.logIfPossible(it.javaClass.name) }
+ else emptyList()
+
+ val blurRadius: Flow<Float> =
+ listOf(
+ *bouncerBlurRadiusFlows.toTypedArray(),
+ *glanceableHubBlurRadiusFlows.toTypedArray(),
+ blurInteractor.blurRadius.map { it.toFloat() }.logIfPossible("ShadeBlur"),
+ )
+ .merge()
+
+ val isBlurOpaque =
+ blurInteractor.isBlurOpaque.distinctUntilChanged().logIfPossible("isBlurOpaque")
override suspend fun onActivated(): Nothing {
coroutineScope {
@@ -60,49 +79,6 @@ constructor(
blurInteractor.onBlurApplied(event)
}
}
-
- launchTraced("WindowRootViewModel#blurState") {
- combine(blurInteractor.blurRadius, blurInteractor.isBlurOpaque, ::BlurState)
- .collect { _blurState.value = it }
- }
-
- launchTraced("WindowRootViewModel#bouncerTransitions") {
- primaryBouncerTransitions
- .map { transition ->
- transition.windowBlurRadius.onEach { blurRadius ->
- if (isLoggable) {
- Log.d(
- TAG,
- "${transition.javaClass.simpleName} windowBlurRadius $blurRadius",
- )
- }
- }
- }
- .merge()
- .collect { blurRadius ->
- blurInteractor.requestBlurForBouncer(blurRadius.toInt())
- }
- }
-
- if (glanceableHubBlurredBackground()) {
- launchTraced("WindowRootViewModel#glanceableHubTransitions") {
- glanceableHubTransitions
- .map { transition ->
- transition.windowBlurRadius.onEach { blurRadius ->
- if (isLoggable) {
- Log.d(
- TAG,
- "${transition.javaClass.simpleName} windowBlurRadius $blurRadius",
- )
- }
- }
- }
- .merge()
- .collect { blurRadius ->
- blurInteractor.requestBlurForGlanceableHub(blurRadius.toInt())
- }
- }
- }
}
awaitCancellation()
}
@@ -118,7 +94,11 @@ constructor(
private companion object {
const val TAG = "WindowRootViewModel"
- val isLoggable = Log.isLoggable(TAG, Log.DEBUG) || Build.isDebuggable()
+ val isLoggable = Log.isLoggable(TAG, Log.VERBOSE) || Build.isDebuggable()
+
+ fun <T> Flow<T>.logIfPossible(loggingInfo: String): Flow<T> {
+ return onEach { if (isLoggable) Log.v(TAG, "$loggingInfo $it") }
+ }
}
}