Make ViewModels consistent in terms of using ExclusiveActivateable
It's way more convinient to use flows in the View because of the
animations. This CL makes sure all Volume Dialog ViewModels follow the
same pattern of exposing properties as Flow.
This CL doesn't introduce new changes, but reorganizes the existing
code.
Flag: com.android.systemui.volume_redesign
Bug: 369994090
Test: manual on the foldable
Change-Id: I1818de4eee266edb70fc6216039a3db288230cb8
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt
index 1a19806..b912361 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt
@@ -16,6 +16,7 @@
package com.android.systemui.volume.dialog
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.plugins.VolumeDialog
import com.android.systemui.volume.dialog.dagger.VolumeDialogPluginComponent
@@ -23,7 +24,6 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.coroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
class VolumeDialogPlugin
@Inject
@@ -33,19 +33,22 @@
) : VolumeDialog {
private var job: Job? = null
+ private var pluginComponent: VolumeDialogPluginComponent? = null
override fun init(windowType: Int, callback: VolumeDialog.Callback?) {
job =
applicationCoroutineScope.launch {
coroutineScope {
- val component = volumeDialogPluginComponentFactory.create(this)
-
- component.viewModel().activate()
+ pluginComponent =
+ volumeDialogPluginComponentFactory.create(this).also {
+ it.viewModel().launchVolumeDialog()
+ }
}
}
}
override fun destroy() {
job?.cancel()
+ pluginComponent = null
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt
index fb108c5..7307807 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt
@@ -17,6 +17,7 @@
package com.android.systemui.volume.dialog.domain.interactor
import android.annotation.SuppressLint
+import com.android.systemui.plugins.VolumeDialogController
import com.android.systemui.volume.Events
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPlugin
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPluginScope
@@ -34,11 +35,14 @@
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.stateIn
private val MAX_DIALOG_SHOW_TIME: Duration = 3.seconds
@@ -57,11 +61,16 @@
callbacksInteractor: VolumeDialogCallbacksInteractor,
private val tracer: VolumeTracer,
private val repository: VolumeDialogVisibilityRepository,
+ private val controller: VolumeDialogController,
) {
@SuppressLint("SharedFlowCreation")
private val mutableDismissDialogEvents = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
- val dialogVisibility: Flow<VolumeDialogVisibilityModel> = repository.dialogVisibility
+ val dialogVisibility: Flow<VolumeDialogVisibilityModel> =
+ repository.dialogVisibility
+ .onEach { controller.notifyVisible(it is Visible) }
+ .stateIn(coroutineScope, SharingStarted.Eagerly, null)
+ .filterNotNull()
init {
merge(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt
index b2f6cb3..2e1f82d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt
@@ -26,6 +26,8 @@
import com.android.systemui.volume.dialog.settings.ui.viewmodel.VolumeDialogSettingsButtonViewModel
import javax.inject.Inject
import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
@VolumeDialogScope
class VolumeDialogSettingsButtonViewBinder
@@ -42,7 +44,12 @@
factory = { viewModelFactory.create() },
) { viewModel ->
setSnapshotBinding {
- visibility = if (viewModel.isVisible) View.VISIBLE else View.GONE
+ viewModel.isVisible
+ .onEach { isVisible ->
+ visibility = if (isVisible) View.VISIBLE else View.GONE
+ }
+ .launchIn(this)
+
button.setOnClickListener { viewModel.onButtonClicked() }
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt
index 2acc33b..015d773 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt
@@ -16,9 +16,6 @@
package com.android.systemui.volume.dialog.settings.ui.viewmodel
-import androidx.compose.runtime.getValue
-import com.android.systemui.lifecycle.ExclusiveActivatable
-import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
import com.android.systemui.volume.dialog.settings.domain.VolumeDialogSettingsButtonInteractor
import dagger.assisted.AssistedFactory
@@ -26,15 +23,9 @@
class VolumeDialogSettingsButtonViewModel
@AssistedInject
-constructor(private val interactor: VolumeDialogSettingsButtonInteractor) : ExclusiveActivatable() {
+constructor(private val interactor: VolumeDialogSettingsButtonInteractor) {
- private val hydrator = Hydrator("VolumeDialog_settings_button")
-
- val isVisible by hydrator.hydratedStateOf("isVisible", interactor.isVisible)
-
- override suspend fun onActivated(): Nothing {
- hydrator.activate()
- }
+ val isVisible = interactor.isVisible
fun onButtonClicked() {
interactor.onButtonClicked()
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
index f486fe1..0a4e3f4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
@@ -23,14 +23,14 @@
import androidx.compose.ui.util.fastForEachIndexed
import com.android.systemui.lifecycle.WindowLifecycleState
import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.lifecycle.setSnapshotBinding
import com.android.systemui.lifecycle.viewModel
import com.android.systemui.res.R
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSlidersViewModel
import javax.inject.Inject
import kotlin.math.abs
-import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
@VolumeDialogScope
class VolumeDialogSlidersViewBinder
@@ -48,20 +48,20 @@
minWindowLifecycleState = WindowLifecycleState.ATTACHED,
factory = { viewModelFactory.create() },
) { viewModel ->
- setSnapshotBinding {
- viewModel.uiModel?.sliderViewBinder?.bind(volumeDialog)
+ viewModel.sliders
+ .onEach { uiModel ->
+ uiModel.sliderViewBinder.bind(volumeDialog)
- val floatingSliderViewBinders =
- viewModel.uiModel?.floatingSliderViewBinders ?: emptyList()
- floatingSlidersContainer.ensureChildCount(
- viewLayoutId = R.layout.volume_dialog_slider_floating,
- count = floatingSliderViewBinders.size,
- )
- floatingSliderViewBinders.fastForEachIndexed { index, viewBinder ->
- viewBinder.bind(floatingSlidersContainer.getChildAt(index))
+ val floatingSliderViewBinders = uiModel.floatingSliderViewBinders
+ floatingSlidersContainer.ensureChildCount(
+ viewLayoutId = R.layout.volume_dialog_slider_floating,
+ count = floatingSliderViewBinders.size,
+ )
+ floatingSliderViewBinders.fastForEachIndexed { index, viewBinder ->
+ viewBinder.bind(floatingSlidersContainer.getChildAt(index))
+ }
}
- }
- awaitCancellation()
+ .launchIn(this)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
index 22cf89f..b5b292f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
@@ -16,9 +16,6 @@
package com.android.systemui.volume.dialog.sliders.ui.viewmodel
-import androidx.compose.runtime.getValue
-import com.android.systemui.lifecycle.ExclusiveActivatable
-import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInteractor
import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSlidersInteractor
@@ -27,9 +24,10 @@
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@@ -41,10 +39,9 @@
private val sliderInteractorFactory: VolumeDialogSliderInteractor.Factory,
private val sliderViewModelFactory: VolumeDialogSliderViewModel.Factory,
private val sliderViewBinderFactory: VolumeDialogSliderViewBinder.Factory,
-) : ExclusiveActivatable() {
+) {
- private val hydrator = Hydrator("VolumeDialogSlidersViewModel")
- private val slidersStateFlow: StateFlow<VolumeDialogSliderUiModel?> =
+ val sliders: Flow<VolumeDialogSliderUiModel> =
slidersInteractor.sliders
.distinctUntilChanged()
.map { slidersModel ->
@@ -55,13 +52,7 @@
)
}
.stateIn(coroutineScope, SharingStarted.Eagerly, null)
-
- val uiModel: VolumeDialogSliderUiModel? by
- hydrator.hydratedStateOf("VolumeDialogSlidersViewModel#uiModel", slidersStateFlow)
-
- override suspend fun onActivated(): Nothing {
- hydrator.activate()
- }
+ .filterNotNull()
private fun createSliderViewBinder(type: VolumeDialogSliderType): VolumeDialogSliderViewBinder =
sliderViewBinderFactory.create {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogResourcesViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/VolumeDialogResources.kt
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogResourcesViewModel.kt
rename to packages/SystemUI/src/com/android/systemui/volume/dialog/ui/VolumeDialogResources.kt
index da9be98..e5cf62b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogResourcesViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/VolumeDialogResources.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.volume.dialog.ui.viewmodel
+package com.android.systemui.volume.dialog.ui
import android.content.Context
import android.content.res.Resources
@@ -41,7 +41,7 @@
* Consume or use [kotlinx.coroutines.flow.first] to get the value.
*/
@VolumeDialogScope
-class VolumeDialogResourcesViewModel
+class VolumeDialogResources
@Inject
constructor(
@VolumeDialog private val coroutineScope: CoroutineScope,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
index f9c7bc0..78eabb2 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
@@ -37,10 +37,10 @@
import com.android.systemui.volume.dialog.settings.ui.binder.VolumeDialogSettingsButtonViewBinder
import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSlidersViewBinder
+import com.android.systemui.volume.dialog.ui.VolumeDialogResources
import com.android.systemui.volume.dialog.ui.utils.JankListenerFactory
import com.android.systemui.volume.dialog.ui.utils.suspendAnimate
import com.android.systemui.volume.dialog.ui.viewmodel.VolumeDialogGravityViewModel
-import com.android.systemui.volume.dialog.ui.viewmodel.VolumeDialogResourcesViewModel
import com.android.systemui.volume.dialog.ui.viewmodel.VolumeDialogViewModel
import com.android.systemui.volume.dialog.utils.VolumeTracer
import javax.inject.Inject
@@ -59,7 +59,7 @@
class VolumeDialogViewBinder
@Inject
constructor(
- private val volumeResources: VolumeDialogResourcesViewModel,
+ private val volumeResources: VolumeDialogResources,
private val gravityViewModel: VolumeDialogGravityViewModel,
private val viewModelFactory: VolumeDialogViewModel.Factory,
private val jankListenerFactory: JankListenerFactory,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
index f336d46..e858cfe 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
@@ -16,8 +16,6 @@
package com.android.systemui.volume.dialog.ui.viewmodel
-import com.android.systemui.lifecycle.ExclusiveActivatable
-import com.android.systemui.plugins.VolumeDialogController
import com.android.systemui.volume.Events
import com.android.systemui.volume.dialog.dagger.VolumeDialogComponent
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPluginScope
@@ -26,12 +24,10 @@
import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.cancel
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.mapLatest
-import kotlinx.coroutines.flow.onEach
@OptIn(ExperimentalCoroutinesApi::class)
@VolumeDialogPluginScope
@@ -40,18 +36,18 @@
constructor(
private val componentFactory: VolumeDialogComponent.Factory,
private val dialogVisibilityInteractor: VolumeDialogVisibilityInteractor,
- private val controller: VolumeDialogController,
private val logger: VolumeDialogLogger,
-) : ExclusiveActivatable() {
+) {
- override suspend fun onActivated(): Nothing {
+ suspend fun launchVolumeDialog() {
coroutineScope {
dialogVisibilityInteractor.dialogVisibility
- .onEach { controller.notifyVisible(it is VolumeDialogVisibilityModel.Visible) }
.mapLatest { visibilityModel ->
with(visibilityModel) {
if (this is VolumeDialogVisibilityModel.Visible) {
- showDialog(reason, keyguardLocked)
+ showDialog(componentFactory)
+ Events.writeEvent(Events.EVENT_SHOW_DIALOG, reason, keyguardLocked)
+ logger.onShow(reason)
}
if (this is VolumeDialogVisibilityModel.Dismissed) {
Events.writeEvent(Events.EVENT_DISMISS_DIALOG, reason)
@@ -61,24 +57,18 @@
}
.launchIn(this)
}
- awaitCancellation()
}
- suspend fun showDialog(reason: Int, keyguardLocked: Boolean): Unit = coroutineScope {
- logger.onShow(reason)
-
- controller.notifyVisible(true)
-
- val volumeDialogComponent: VolumeDialogComponent = componentFactory.create(this)
- val dialog =
- volumeDialogComponent.volumeDialog().apply {
- setOnDismissListener {
- volumeDialogComponent.coroutineScope().cancel()
- dialogVisibilityInteractor.dismissDialog(Events.DISMISS_REASON_UNKNOWN)
+ private suspend fun showDialog(componentFactory: VolumeDialogComponent.Factory): Unit =
+ coroutineScope {
+ val volumeDialogComponent: VolumeDialogComponent = componentFactory.create(this)
+ val dialog =
+ volumeDialogComponent.volumeDialog().apply {
+ setOnDismissListener {
+ volumeDialogComponent.coroutineScope().cancel()
+ dialogVisibilityInteractor.dismissDialog(Events.DISMISS_REASON_UNKNOWN)
+ }
}
- }
- dialog.show()
-
- Events.writeEvent(Events.EVENT_SHOW_DIALOG, reason, keyguardLocked)
- }
+ dialog.show()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
index e9786d0..9be669f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
@@ -17,7 +17,6 @@
package com.android.systemui.volume.dialog.ui.viewmodel
import android.content.Context
-import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.res.R
import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
@@ -27,7 +26,6 @@
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
@@ -41,7 +39,7 @@
dialogVisibilityInteractor: VolumeDialogVisibilityInteractor,
volumeDialogSlidersInteractor: VolumeDialogSlidersInteractor,
private val volumeDialogSliderInteractorFactory: VolumeDialogSliderInteractor.Factory,
-) : ExclusiveActivatable() {
+) {
val dialogVisibilityModel: Flow<VolumeDialogVisibilityModel> =
dialogVisibilityInteractor.dialogVisibility
@@ -53,10 +51,6 @@
}
}
- override suspend fun onActivated(): Nothing {
- awaitCancellation()
- }
-
@AssistedFactory
interface Factory {
fun create(): VolumeDialogViewModel
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorKosmos.kt
index 7376c7f..0d2aa4c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorKosmos.kt
@@ -18,6 +18,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.plugins.volumeDialogController
import com.android.systemui.volume.dialog.data.repository.volumeDialogVisibilityRepository
import com.android.systemui.volume.dialog.utils.volumeTracer
@@ -28,5 +29,6 @@
volumeDialogCallbacksInteractor,
volumeTracer,
volumeDialogVisibilityRepository,
+ volumeDialogController,
)
}