Merge changes I1818de4e,If8033432 into main

* changes:
  Make ViewModels consistent in terms of using ExclusiveActivateable
  Add Volume Dialog window title based on the currently active slider.
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt
index 7476c6a..097a60f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt
@@ -25,20 +25,20 @@
 import com.android.systemui.res.R
 import com.android.systemui.volume.Events
 import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
-import com.android.systemui.volume.dialog.ui.binder.VolumeDialogBinder
+import com.android.systemui.volume.dialog.ui.binder.VolumeDialogViewBinder
 import javax.inject.Inject
 
 class VolumeDialog
 @Inject
 constructor(
     @Application context: Context,
-    private val dialogBinder: VolumeDialogBinder,
+    private val viewBinder: VolumeDialogViewBinder,
     private val visibilityInteractor: VolumeDialogVisibilityInteractor,
 ) : Dialog(ContextThemeWrapper(context, R.style.volume_dialog_theme)) {
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        dialogBinder.bind(this)
+        viewBinder.bind(this)
     }
 
     /**
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/shared/model/VolumeDialogStreamModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogStreamModel.kt
index be3cd97..cec58c3 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogStreamModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogStreamModel.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.volume.dialog.shared.model
 
+import android.content.Context
 import androidx.annotation.StringRes
 import com.android.systemui.plugins.VolumeDialogController
 
@@ -29,7 +30,9 @@
     val levelMax: Int = 0,
     val muted: Boolean = false,
     val muteSupported: Boolean = false,
+    /** You likely need to use [streamLabel] instead. */
     @StringRes val name: Int = 0,
+    /** You likely need to use [streamLabel] instead. */
     val remoteLabel: String? = null,
     val routedToBluetooth: Boolean = false,
 ) {
@@ -51,3 +54,10 @@
         routedToBluetooth = legacyState.routedToBluetooth,
     )
 }
+
+fun VolumeDialogStreamModel.streamLabel(context: Context): String {
+    if (remoteLabel != null) {
+        return remoteLabel
+    }
+    return context.resources.getString(name)
+}
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/VolumeDialogBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt
deleted file mode 100644
index cd535e4..0000000
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt
+++ /dev/null
@@ -1,93 +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.volume.dialog.ui.binder
-
-import android.app.Dialog
-import android.graphics.Color
-import android.graphics.PixelFormat
-import android.graphics.drawable.ColorDrawable
-import android.view.View
-import android.view.ViewGroup
-import android.view.Window
-import android.view.WindowManager
-import com.android.systemui.res.R
-import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
-import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
-import com.android.systemui.volume.dialog.ringer.ui.binder.VolumeDialogRingerViewBinder
-import com.android.systemui.volume.dialog.settings.ui.binder.VolumeDialogSettingsButtonViewBinder
-import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSlidersViewBinder
-import com.android.systemui.volume.dialog.ui.viewmodel.VolumeDialogGravityViewModel
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-
-/** Binds the Volume Dialog itself. */
-@VolumeDialogScope
-class VolumeDialogBinder
-@Inject
-constructor(
-    @VolumeDialog private val coroutineScope: CoroutineScope,
-    private val volumeDialogViewBinder: VolumeDialogViewBinder,
-    private val slidersViewBinder: VolumeDialogSlidersViewBinder,
-    private val volumeDialogRingerViewBinder: VolumeDialogRingerViewBinder,
-    private val settingsButtonViewBinder: VolumeDialogSettingsButtonViewBinder,
-    private val gravityViewModel: VolumeDialogGravityViewModel,
-) {
-
-    fun bind(dialog: Dialog) {
-        with(dialog) {
-            setupWindow(window!!)
-            dialog.setContentView(R.layout.volume_dialog)
-            dialog.setCanceledOnTouchOutside(true)
-
-            with(dialog.requireViewById<View>(R.id.volume_dialog_container)) {
-                volumeDialogRingerViewBinder.bind(this)
-                slidersViewBinder.bind(this)
-                settingsButtonViewBinder.bind(this)
-                volumeDialogViewBinder.bind(dialog, this)
-            }
-        }
-    }
-
-    /** Configures [Window] for the [Dialog]. */
-    private fun setupWindow(window: Window) =
-        with(window) {
-            clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
-            addFlags(
-                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
-                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
-                    WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH or
-                    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
-            )
-            addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY)
-
-            requestFeature(Window.FEATURE_NO_TITLE)
-            setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
-            setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY)
-            setWindowAnimations(-1)
-            setFormat(PixelFormat.TRANSLUCENT)
-
-            attributes =
-                attributes.apply {
-                    title = "VolumeDialog" // Not the same as Window#setTitle
-                }
-            setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
-
-            gravityViewModel.dialogGravity.onEach { window.setGravity(it) }.launchIn(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 23e6eac..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
@@ -17,19 +17,30 @@
 package com.android.systemui.volume.dialog.ui.binder
 
 import android.app.Dialog
+import android.graphics.Color
+import android.graphics.PixelFormat
+import android.graphics.drawable.ColorDrawable
 import android.view.Gravity
 import android.view.View
+import android.view.ViewGroup
+import android.view.Window
+import android.view.WindowManager
 import com.android.internal.view.RotationPolicy
 import com.android.systemui.lifecycle.WindowLifecycleState
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.lifecycle.viewModel
+import com.android.systemui.res.R
 import com.android.systemui.volume.SystemUIInterpolators
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import com.android.systemui.volume.dialog.ringer.ui.binder.VolumeDialogRingerViewBinder
+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
@@ -40,6 +51,7 @@
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.flow.onEach
 
 /** Binds the root view of the Volume Dialog. */
 @OptIn(ExperimentalCoroutinesApi::class)
@@ -47,14 +59,20 @@
 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,
     private val tracer: VolumeTracer,
+    @VolumeDialog private val coroutineScope: CoroutineScope,
+    private val volumeDialogRingerViewBinder: VolumeDialogRingerViewBinder,
+    private val slidersViewBinder: VolumeDialogSlidersViewBinder,
+    private val settingsButtonViewBinder: VolumeDialogSettingsButtonViewBinder,
 ) {
 
-    fun bind(dialog: Dialog, view: View) {
+    fun bind(dialog: Dialog) {
+        setupDialog(dialog)
+        val view: View = dialog.requireViewById(R.id.volume_dialog_container)
         view.alpha = 0f
         view.repeatWhenAttached {
             view.viewModel(
@@ -62,11 +80,46 @@
                 minWindowLifecycleState = WindowLifecycleState.ATTACHED,
                 factory = { viewModelFactory.create() },
             ) { viewModel ->
+                viewModel.dialogTitle.onEach { dialog.window?.setTitle(it) }.launchIn(this)
+
                 animateVisibility(view, dialog, viewModel.dialogVisibilityModel)
 
                 awaitCancellation()
             }
         }
+        volumeDialogRingerViewBinder.bind(view)
+        slidersViewBinder.bind(view)
+        settingsButtonViewBinder.bind(view)
+    }
+
+    /** Configures [Window] for the [Dialog]. */
+    private fun setupDialog(dialog: Dialog) {
+        with(dialog.window!!) {
+            clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
+            addFlags(
+                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
+                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
+                    WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH or
+                    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
+            )
+            addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY)
+
+            requestFeature(Window.FEATURE_NO_TITLE)
+            setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+            setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY)
+            setWindowAnimations(-1)
+            setFormat(PixelFormat.TRANSLUCENT)
+
+            attributes =
+                attributes.apply {
+                    title = "VolumeDialog" // Not the same as Window#setTitle
+                }
+            setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
+
+            gravityViewModel.dialogGravity.onEach { setGravity(it) }.launchIn(coroutineScope)
+        }
+        dialog.setContentView(R.layout.volume_dialog)
+        dialog.setCanceledOnTouchOutside(true)
     }
 
     private fun CoroutineScope.animateVisibility(
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 84c837c..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
@@ -16,25 +16,40 @@
 
 package com.android.systemui.volume.dialog.ui.viewmodel
 
-import com.android.systemui.lifecycle.ExclusiveActivatable
+import android.content.Context
+import com.android.systemui.res.R
 import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
 import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
+import com.android.systemui.volume.dialog.shared.model.streamLabel
+import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInteractor
+import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSlidersInteractor
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
-import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
 
 /** Provides a state for the Volume Dialog. */
+@OptIn(ExperimentalCoroutinesApi::class)
 class VolumeDialogViewModel
 @AssistedInject
-constructor(dialogVisibilityInteractor: VolumeDialogVisibilityInteractor) : ExclusiveActivatable() {
+constructor(
+    private val context: Context,
+    dialogVisibilityInteractor: VolumeDialogVisibilityInteractor,
+    volumeDialogSlidersInteractor: VolumeDialogSlidersInteractor,
+    private val volumeDialogSliderInteractorFactory: VolumeDialogSliderInteractor.Factory,
+) {
 
     val dialogVisibilityModel: Flow<VolumeDialogVisibilityModel> =
         dialogVisibilityInteractor.dialogVisibility
-
-    override suspend fun onActivated(): Nothing {
-        awaitCancellation()
-    }
+    val dialogTitle: Flow<String> =
+        volumeDialogSlidersInteractor.sliders.flatMapLatest { slidersModel ->
+            val interactor = volumeDialogSliderInteractorFactory.create(slidersModel.slider)
+            interactor.slider.map { sliderModel ->
+                context.getString(R.string.volume_dialog_title, sliderModel.streamLabel(context))
+            }
+        }
 
     @AssistedFactory
     interface Factory {
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,
         )
     }