Loading packages/SystemUI/res/layout/volume_dialog.xml +47 −39 Original line number Original line Diff line number Diff line Loading @@ -13,16 +13,20 @@ See the License for the specific language governing permissions and See the License for the specific language governing permissions and limitations under the License. limitations under the License. --> --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:id="@+id/volume_dialog_root" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:id="@+id/volume_dialog_container" android:id="@+id/volume_dialog_container" android:layout_width="wrap_content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" android:layout_gravity="center_vertical|end" android:divider="@drawable/volume_dialog_floating_sliders_spacer" android:divider="@drawable/volume_dialog_floating_sliders_spacer" android:orientation="horizontal" android:orientation="horizontal" android:showDividers="middle|end|beginning" android:showDividers="middle|end|beginning"> android:theme="@style/volume_dialog_theme"> <LinearLayout <LinearLayout android:id="@+id/volume_dialog_floating_sliders_container" android:id="@+id/volume_dialog_floating_sliders_container" Loading @@ -39,10 +43,13 @@ android:layout_width="@dimen/volume_dialog_width" android:layout_width="@dimen/volume_dialog_width" android:layout_height="wrap_content" android:layout_height="wrap_content" android:background="@drawable/volume_dialog_background" android:background="@drawable/volume_dialog_background" android:clipChildren="false" android:clipToOutline="false" android:clipToPadding="false" android:divider="@drawable/volume_dialog_spacer" android:divider="@drawable/volume_dialog_spacer" android:paddingVertical="@dimen/volume_dialog_vertical_padding" android:gravity="center_horizontal" android:gravity="center_horizontal" android:orientation="vertical" android:orientation="vertical" android:paddingVertical="@dimen/volume_dialog_vertical_padding" android:showDividers="middle"> android:showDividers="middle"> <include layout="@layout/volume_ringer_drawer" /> <include layout="@layout/volume_ringer_drawer" /> Loading @@ -60,3 +67,4 @@ android:tint="?androidprv:attr/materialColorPrimary" /> android:tint="?androidprv:attr/materialColorPrimary" /> </LinearLayout> </LinearLayout> </LinearLayout> </LinearLayout> </FrameLayout> No newline at end of file packages/SystemUI/res/values/styles.xml +10 −0 Original line number Original line Diff line number Diff line Loading @@ -557,6 +557,16 @@ <item name="android:showWhenLocked">true</item> <item name="android:showWhenLocked">true</item> </style> </style> <style name="Theme.SystemUI.Dialog.Volume"> <item name="android:backgroundDimEnabled">false</item> <item name="android:showWhenLocked">true</item> <item name="android:windowBackground">@color/transparent</item> <item name="android:windowContentOverlay">@null</item> <item name="android:windowFullscreen">true</item> <item name="android:windowIsFloating">false</item> <item name="android:windowNoTitle">true</item> </style> <style name="SystemUI.Material3.Slider.Volume"> <style name="SystemUI.Material3.Slider.Volume"> <item name="trackHeight">40dp</item> <item name="trackHeight">40dp</item> <item name="thumbHeight">52dp</item> <item name="thumbHeight">52dp</item> Loading packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt +29 −1 Original line number Original line Diff line number Diff line Loading @@ -18,9 +18,13 @@ package com.android.systemui.volume.dialog import android.app.Dialog import android.app.Dialog import android.content.Context import android.content.Context import android.graphics.PixelFormat import android.os.Bundle import android.os.Bundle import android.view.MotionEvent import android.view.MotionEvent import android.view.ViewGroup import android.view.WindowManager import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.res.R import com.android.systemui.volume.Events import com.android.systemui.volume.Events import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor import com.android.systemui.volume.dialog.ui.binder.VolumeDialogViewBinder import com.android.systemui.volume.dialog.ui.binder.VolumeDialogViewBinder Loading @@ -32,10 +36,34 @@ constructor( @Application context: Context, @Application context: Context, private val viewBinder: VolumeDialogViewBinder, private val viewBinder: VolumeDialogViewBinder, private val visibilityInteractor: VolumeDialogVisibilityInteractor, private val visibilityInteractor: VolumeDialogVisibilityInteractor, ) : Dialog(context) { ) : Dialog(context, R.style.Theme_SystemUI_Dialog_Volume) { init { with(window!!) { 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) 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.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) } setCanceledOnTouchOutside(true) } override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) super.onCreate(savedInstanceState) setContentView(R.layout.volume_dialog) viewBinder.bind(this) viewBinder.bind(this) } } Loading packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt +56 −48 Original line number Original line Diff line number Diff line Loading @@ -17,21 +17,22 @@ package com.android.systemui.volume.dialog.ui.binder package com.android.systemui.volume.dialog.ui.binder import android.app.Dialog import android.app.Dialog import android.graphics.Color import android.graphics.Rect import android.graphics.PixelFormat import android.graphics.Region import android.graphics.drawable.ColorDrawable import android.view.Gravity import android.view.Gravity import android.view.View import android.view.View import android.view.ViewGroup import android.view.ViewGroup import android.view.Window import android.view.ViewTreeObserver import android.view.WindowManager import android.view.ViewTreeObserver.InternalInsetsInfo import android.widget.FrameLayout import androidx.annotation.GravityInt import com.android.internal.view.RotationPolicy import com.android.internal.view.RotationPolicy import com.android.systemui.lifecycle.WindowLifecycleState import com.android.systemui.lifecycle.WindowLifecycleState import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.lifecycle.viewModel import com.android.systemui.lifecycle.viewModel import com.android.systemui.res.R import com.android.systemui.res.R import com.android.systemui.util.children import com.android.systemui.volume.SystemUIInterpolators 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.dagger.scope.VolumeDialogScope import com.android.systemui.volume.dialog.ringer.ui.binder.VolumeDialogRingerViewBinder 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.settings.ui.binder.VolumeDialogSettingsButtonViewBinder Loading @@ -52,6 +53,8 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.suspendCancellableCoroutine /** Binds the root view of the Volume Dialog. */ /** Binds the root view of the Volume Dialog. */ @OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class) Loading @@ -61,65 +64,41 @@ class VolumeDialogViewBinder constructor( constructor( private val volumeResources: VolumeDialogResources, private val volumeResources: VolumeDialogResources, private val gravityViewModel: VolumeDialogGravityViewModel, private val gravityViewModel: VolumeDialogGravityViewModel, private val viewModelFactory: VolumeDialogViewModel.Factory, private val dialogViewModelFactory: VolumeDialogViewModel.Factory, private val jankListenerFactory: JankListenerFactory, private val jankListenerFactory: JankListenerFactory, private val tracer: VolumeTracer, private val tracer: VolumeTracer, @VolumeDialog private val coroutineScope: CoroutineScope, private val volumeDialogRingerViewBinder: VolumeDialogRingerViewBinder, private val volumeDialogRingerViewBinder: VolumeDialogRingerViewBinder, private val slidersViewBinder: VolumeDialogSlidersViewBinder, private val slidersViewBinder: VolumeDialogSlidersViewBinder, private val settingsButtonViewBinder: VolumeDialogSettingsButtonViewBinder, private val settingsButtonViewBinder: VolumeDialogSettingsButtonViewBinder, ) { ) { fun bind(dialog: Dialog) { fun bind(dialog: Dialog) { setupDialog(dialog) // Root view of the Volume Dialog. val view: View = dialog.requireViewById(R.id.volume_dialog_container) val root: ViewGroup = dialog.requireViewById(R.id.volume_dialog_root) view.alpha = 0f // Volume Dialog container view that contains the dialog itself without the floating sliders view.repeatWhenAttached { val container: View = root.requireViewById(R.id.volume_dialog_container) view.viewModel( container.alpha = 0f container.repeatWhenAttached { root.viewModel( traceName = "VolumeDialogViewBinder", traceName = "VolumeDialogViewBinder", minWindowLifecycleState = WindowLifecycleState.ATTACHED, minWindowLifecycleState = WindowLifecycleState.ATTACHED, factory = { viewModelFactory.create() }, factory = { dialogViewModelFactory.create() }, ) { viewModel -> ) { viewModel -> animateVisibility(container, dialog, viewModel.dialogVisibilityModel) viewModel.dialogTitle.onEach { dialog.window?.setTitle(it) }.launchIn(this) viewModel.dialogTitle.onEach { dialog.window?.setTitle(it) }.launchIn(this) gravityViewModel.dialogGravity .onEach { container.setLayoutGravity(it) } .launchIn(this) animateVisibility(view, dialog, viewModel.dialogVisibilityModel) launch { root.viewTreeObserver.computeInternalInsetsListener(root) } awaitCancellation() awaitCancellation() } } } } volumeDialogRingerViewBinder.bind(view) volumeDialogRingerViewBinder.bind(root) slidersViewBinder.bind(view) slidersViewBinder.bind(root) settingsButtonViewBinder.bind(view) settingsButtonViewBinder.bind(root) } /** 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( private fun CoroutineScope.animateVisibility( Loading Loading @@ -209,4 +188,33 @@ constructor( } } animator.suspendAnimate(jankListenerFactory.dismiss(this, duration)) animator.suspendAnimate(jankListenerFactory.dismiss(this, duration)) } } private suspend fun ViewTreeObserver.computeInternalInsetsListener(viewGroup: ViewGroup) = suspendCancellableCoroutine<Unit> { continuation -> val listener = ViewTreeObserver.OnComputeInternalInsetsListener { inoutInfo -> viewGroup.fillTouchableBounds(inoutInfo) } addOnComputeInternalInsetsListener(listener) continuation.invokeOnCancellation { removeOnComputeInternalInsetsListener(listener) } } private fun ViewGroup.fillTouchableBounds(internalInsetsInfo: InternalInsetsInfo) { for (child in children) { val boundsRect = Rect() internalInsetsInfo.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION) child.getBoundsInWindow(boundsRect, false) internalInsetsInfo.touchableRegion.op(boundsRect, Region.Op.UNION) } val boundsRect = Rect() getBoundsInWindow(boundsRect, false) } private fun View.setLayoutGravity(@GravityInt newGravity: Int) { val frameLayoutParams = layoutParams as? FrameLayout.LayoutParams ?: error("View must be a child of a FrameLayout") layoutParams = frameLayoutParams.apply { gravity = newGravity } } } } Loading
packages/SystemUI/res/layout/volume_dialog.xml +47 −39 Original line number Original line Diff line number Diff line Loading @@ -13,16 +13,20 @@ See the License for the specific language governing permissions and See the License for the specific language governing permissions and limitations under the License. limitations under the License. --> --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:id="@+id/volume_dialog_root" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:id="@+id/volume_dialog_container" android:id="@+id/volume_dialog_container" android:layout_width="wrap_content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" android:layout_gravity="center_vertical|end" android:divider="@drawable/volume_dialog_floating_sliders_spacer" android:divider="@drawable/volume_dialog_floating_sliders_spacer" android:orientation="horizontal" android:orientation="horizontal" android:showDividers="middle|end|beginning" android:showDividers="middle|end|beginning"> android:theme="@style/volume_dialog_theme"> <LinearLayout <LinearLayout android:id="@+id/volume_dialog_floating_sliders_container" android:id="@+id/volume_dialog_floating_sliders_container" Loading @@ -39,10 +43,13 @@ android:layout_width="@dimen/volume_dialog_width" android:layout_width="@dimen/volume_dialog_width" android:layout_height="wrap_content" android:layout_height="wrap_content" android:background="@drawable/volume_dialog_background" android:background="@drawable/volume_dialog_background" android:clipChildren="false" android:clipToOutline="false" android:clipToPadding="false" android:divider="@drawable/volume_dialog_spacer" android:divider="@drawable/volume_dialog_spacer" android:paddingVertical="@dimen/volume_dialog_vertical_padding" android:gravity="center_horizontal" android:gravity="center_horizontal" android:orientation="vertical" android:orientation="vertical" android:paddingVertical="@dimen/volume_dialog_vertical_padding" android:showDividers="middle"> android:showDividers="middle"> <include layout="@layout/volume_ringer_drawer" /> <include layout="@layout/volume_ringer_drawer" /> Loading @@ -60,3 +67,4 @@ android:tint="?androidprv:attr/materialColorPrimary" /> android:tint="?androidprv:attr/materialColorPrimary" /> </LinearLayout> </LinearLayout> </LinearLayout> </LinearLayout> </FrameLayout> No newline at end of file
packages/SystemUI/res/values/styles.xml +10 −0 Original line number Original line Diff line number Diff line Loading @@ -557,6 +557,16 @@ <item name="android:showWhenLocked">true</item> <item name="android:showWhenLocked">true</item> </style> </style> <style name="Theme.SystemUI.Dialog.Volume"> <item name="android:backgroundDimEnabled">false</item> <item name="android:showWhenLocked">true</item> <item name="android:windowBackground">@color/transparent</item> <item name="android:windowContentOverlay">@null</item> <item name="android:windowFullscreen">true</item> <item name="android:windowIsFloating">false</item> <item name="android:windowNoTitle">true</item> </style> <style name="SystemUI.Material3.Slider.Volume"> <style name="SystemUI.Material3.Slider.Volume"> <item name="trackHeight">40dp</item> <item name="trackHeight">40dp</item> <item name="thumbHeight">52dp</item> <item name="thumbHeight">52dp</item> Loading
packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt +29 −1 Original line number Original line Diff line number Diff line Loading @@ -18,9 +18,13 @@ package com.android.systemui.volume.dialog import android.app.Dialog import android.app.Dialog import android.content.Context import android.content.Context import android.graphics.PixelFormat import android.os.Bundle import android.os.Bundle import android.view.MotionEvent import android.view.MotionEvent import android.view.ViewGroup import android.view.WindowManager import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.res.R import com.android.systemui.volume.Events import com.android.systemui.volume.Events import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor import com.android.systemui.volume.dialog.ui.binder.VolumeDialogViewBinder import com.android.systemui.volume.dialog.ui.binder.VolumeDialogViewBinder Loading @@ -32,10 +36,34 @@ constructor( @Application context: Context, @Application context: Context, private val viewBinder: VolumeDialogViewBinder, private val viewBinder: VolumeDialogViewBinder, private val visibilityInteractor: VolumeDialogVisibilityInteractor, private val visibilityInteractor: VolumeDialogVisibilityInteractor, ) : Dialog(context) { ) : Dialog(context, R.style.Theme_SystemUI_Dialog_Volume) { init { with(window!!) { 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) 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.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) } setCanceledOnTouchOutside(true) } override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) super.onCreate(savedInstanceState) setContentView(R.layout.volume_dialog) viewBinder.bind(this) viewBinder.bind(this) } } Loading
packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt +56 −48 Original line number Original line Diff line number Diff line Loading @@ -17,21 +17,22 @@ package com.android.systemui.volume.dialog.ui.binder package com.android.systemui.volume.dialog.ui.binder import android.app.Dialog import android.app.Dialog import android.graphics.Color import android.graphics.Rect import android.graphics.PixelFormat import android.graphics.Region import android.graphics.drawable.ColorDrawable import android.view.Gravity import android.view.Gravity import android.view.View import android.view.View import android.view.ViewGroup import android.view.ViewGroup import android.view.Window import android.view.ViewTreeObserver import android.view.WindowManager import android.view.ViewTreeObserver.InternalInsetsInfo import android.widget.FrameLayout import androidx.annotation.GravityInt import com.android.internal.view.RotationPolicy import com.android.internal.view.RotationPolicy import com.android.systemui.lifecycle.WindowLifecycleState import com.android.systemui.lifecycle.WindowLifecycleState import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.lifecycle.viewModel import com.android.systemui.lifecycle.viewModel import com.android.systemui.res.R import com.android.systemui.res.R import com.android.systemui.util.children import com.android.systemui.volume.SystemUIInterpolators 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.dagger.scope.VolumeDialogScope import com.android.systemui.volume.dialog.ringer.ui.binder.VolumeDialogRingerViewBinder 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.settings.ui.binder.VolumeDialogSettingsButtonViewBinder Loading @@ -52,6 +53,8 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.suspendCancellableCoroutine /** Binds the root view of the Volume Dialog. */ /** Binds the root view of the Volume Dialog. */ @OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class) Loading @@ -61,65 +64,41 @@ class VolumeDialogViewBinder constructor( constructor( private val volumeResources: VolumeDialogResources, private val volumeResources: VolumeDialogResources, private val gravityViewModel: VolumeDialogGravityViewModel, private val gravityViewModel: VolumeDialogGravityViewModel, private val viewModelFactory: VolumeDialogViewModel.Factory, private val dialogViewModelFactory: VolumeDialogViewModel.Factory, private val jankListenerFactory: JankListenerFactory, private val jankListenerFactory: JankListenerFactory, private val tracer: VolumeTracer, private val tracer: VolumeTracer, @VolumeDialog private val coroutineScope: CoroutineScope, private val volumeDialogRingerViewBinder: VolumeDialogRingerViewBinder, private val volumeDialogRingerViewBinder: VolumeDialogRingerViewBinder, private val slidersViewBinder: VolumeDialogSlidersViewBinder, private val slidersViewBinder: VolumeDialogSlidersViewBinder, private val settingsButtonViewBinder: VolumeDialogSettingsButtonViewBinder, private val settingsButtonViewBinder: VolumeDialogSettingsButtonViewBinder, ) { ) { fun bind(dialog: Dialog) { fun bind(dialog: Dialog) { setupDialog(dialog) // Root view of the Volume Dialog. val view: View = dialog.requireViewById(R.id.volume_dialog_container) val root: ViewGroup = dialog.requireViewById(R.id.volume_dialog_root) view.alpha = 0f // Volume Dialog container view that contains the dialog itself without the floating sliders view.repeatWhenAttached { val container: View = root.requireViewById(R.id.volume_dialog_container) view.viewModel( container.alpha = 0f container.repeatWhenAttached { root.viewModel( traceName = "VolumeDialogViewBinder", traceName = "VolumeDialogViewBinder", minWindowLifecycleState = WindowLifecycleState.ATTACHED, minWindowLifecycleState = WindowLifecycleState.ATTACHED, factory = { viewModelFactory.create() }, factory = { dialogViewModelFactory.create() }, ) { viewModel -> ) { viewModel -> animateVisibility(container, dialog, viewModel.dialogVisibilityModel) viewModel.dialogTitle.onEach { dialog.window?.setTitle(it) }.launchIn(this) viewModel.dialogTitle.onEach { dialog.window?.setTitle(it) }.launchIn(this) gravityViewModel.dialogGravity .onEach { container.setLayoutGravity(it) } .launchIn(this) animateVisibility(view, dialog, viewModel.dialogVisibilityModel) launch { root.viewTreeObserver.computeInternalInsetsListener(root) } awaitCancellation() awaitCancellation() } } } } volumeDialogRingerViewBinder.bind(view) volumeDialogRingerViewBinder.bind(root) slidersViewBinder.bind(view) slidersViewBinder.bind(root) settingsButtonViewBinder.bind(view) settingsButtonViewBinder.bind(root) } /** 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( private fun CoroutineScope.animateVisibility( Loading Loading @@ -209,4 +188,33 @@ constructor( } } animator.suspendAnimate(jankListenerFactory.dismiss(this, duration)) animator.suspendAnimate(jankListenerFactory.dismiss(this, duration)) } } private suspend fun ViewTreeObserver.computeInternalInsetsListener(viewGroup: ViewGroup) = suspendCancellableCoroutine<Unit> { continuation -> val listener = ViewTreeObserver.OnComputeInternalInsetsListener { inoutInfo -> viewGroup.fillTouchableBounds(inoutInfo) } addOnComputeInternalInsetsListener(listener) continuation.invokeOnCancellation { removeOnComputeInternalInsetsListener(listener) } } private fun ViewGroup.fillTouchableBounds(internalInsetsInfo: InternalInsetsInfo) { for (child in children) { val boundsRect = Rect() internalInsetsInfo.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION) child.getBoundsInWindow(boundsRect, false) internalInsetsInfo.touchableRegion.op(boundsRect, Region.Op.UNION) } val boundsRect = Rect() getBoundsInWindow(boundsRect, false) } private fun View.setLayoutGravity(@GravityInt newGravity: Int) { val frameLayoutParams = layoutParams as? FrameLayout.LayoutParams ?: error("View must be a child of a FrameLayout") layoutParams = frameLayoutParams.apply { gravity = newGravity } } } }