Loading packages/SystemUI/res/layout/volume_dialog.xml +47 −39 Original line number Diff line number Diff line Loading @@ -13,16 +13,20 @@ See the License for the specific language governing permissions and 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" android:id="@+id/volume_dialog_root" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:id="@+id/volume_dialog_container" android:layout_width="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:orientation="horizontal" android:showDividers="middle|end|beginning" android:theme="@style/volume_dialog_theme"> android:showDividers="middle|end|beginning"> <LinearLayout android:id="@+id/volume_dialog_floating_sliders_container" Loading @@ -39,10 +43,13 @@ android:layout_width="@dimen/volume_dialog_width" android:layout_height="wrap_content" android:background="@drawable/volume_dialog_background" android:clipChildren="false" android:clipToOutline="false" android:clipToPadding="false" android:divider="@drawable/volume_dialog_spacer" android:paddingVertical="@dimen/volume_dialog_vertical_padding" android:gravity="center_horizontal" android:orientation="vertical" android:paddingVertical="@dimen/volume_dialog_vertical_padding" android:showDividers="middle"> <include layout="@layout/volume_ringer_drawer" /> Loading @@ -60,3 +67,4 @@ android:tint="?androidprv:attr/materialColorPrimary" /> </LinearLayout> </LinearLayout> </FrameLayout> No newline at end of file packages/SystemUI/res/values/styles.xml +10 −0 Original line number Diff line number Diff line Loading @@ -557,6 +557,16 @@ <item name="android:showWhenLocked">true</item> </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"> <item name="trackHeight">40dp</item> <item name="thumbHeight">52dp</item> Loading packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt +29 −1 Original line number Diff line number Diff line Loading @@ -18,9 +18,13 @@ package com.android.systemui.volume.dialog import android.app.Dialog import android.content.Context import android.graphics.PixelFormat import android.os.Bundle import android.view.MotionEvent import android.view.ViewGroup import android.view.WindowManager import com.android.systemui.dagger.qualifiers.Application 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.VolumeDialogViewBinder Loading @@ -32,10 +36,34 @@ constructor( @Application context: Context, private val viewBinder: VolumeDialogViewBinder, 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?) { super.onCreate(savedInstanceState) setContentView(R.layout.volume_dialog) viewBinder.bind(this) } Loading packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt +56 −48 Original line number Diff line number Diff line Loading @@ -17,21 +17,22 @@ 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.graphics.Rect import android.graphics.Region import android.view.Gravity import android.view.View import android.view.ViewGroup import android.view.Window import android.view.WindowManager import android.view.ViewTreeObserver import android.view.ViewTreeObserver.InternalInsetsInfo import android.widget.FrameLayout import androidx.annotation.GravityInt 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.util.children 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 Loading @@ -52,6 +53,8 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.suspendCancellableCoroutine /** Binds the root view of the Volume Dialog. */ @OptIn(ExperimentalCoroutinesApi::class) Loading @@ -61,65 +64,41 @@ class VolumeDialogViewBinder constructor( private val volumeResources: VolumeDialogResources, private val gravityViewModel: VolumeDialogGravityViewModel, private val viewModelFactory: VolumeDialogViewModel.Factory, private val dialogViewModelFactory: 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) { setupDialog(dialog) val view: View = dialog.requireViewById(R.id.volume_dialog_container) view.alpha = 0f view.repeatWhenAttached { view.viewModel( // Root view of the Volume Dialog. val root: ViewGroup = dialog.requireViewById(R.id.volume_dialog_root) // Volume Dialog container view that contains the dialog itself without the floating sliders val container: View = root.requireViewById(R.id.volume_dialog_container) container.alpha = 0f container.repeatWhenAttached { root.viewModel( traceName = "VolumeDialogViewBinder", minWindowLifecycleState = WindowLifecycleState.ATTACHED, factory = { viewModelFactory.create() }, factory = { dialogViewModelFactory.create() }, ) { viewModel -> animateVisibility(container, dialog, viewModel.dialogVisibilityModel) 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() } } 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) volumeDialogRingerViewBinder.bind(root) slidersViewBinder.bind(root) settingsButtonViewBinder.bind(root) } private fun CoroutineScope.animateVisibility( Loading Loading @@ -209,4 +188,33 @@ constructor( } 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 Diff line number Diff line Loading @@ -13,16 +13,20 @@ See the License for the specific language governing permissions and 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" android:id="@+id/volume_dialog_root" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:id="@+id/volume_dialog_container" android:layout_width="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:orientation="horizontal" android:showDividers="middle|end|beginning" android:theme="@style/volume_dialog_theme"> android:showDividers="middle|end|beginning"> <LinearLayout android:id="@+id/volume_dialog_floating_sliders_container" Loading @@ -39,10 +43,13 @@ android:layout_width="@dimen/volume_dialog_width" android:layout_height="wrap_content" android:background="@drawable/volume_dialog_background" android:clipChildren="false" android:clipToOutline="false" android:clipToPadding="false" android:divider="@drawable/volume_dialog_spacer" android:paddingVertical="@dimen/volume_dialog_vertical_padding" android:gravity="center_horizontal" android:orientation="vertical" android:paddingVertical="@dimen/volume_dialog_vertical_padding" android:showDividers="middle"> <include layout="@layout/volume_ringer_drawer" /> Loading @@ -60,3 +67,4 @@ android:tint="?androidprv:attr/materialColorPrimary" /> </LinearLayout> </LinearLayout> </FrameLayout> No newline at end of file
packages/SystemUI/res/values/styles.xml +10 −0 Original line number Diff line number Diff line Loading @@ -557,6 +557,16 @@ <item name="android:showWhenLocked">true</item> </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"> <item name="trackHeight">40dp</item> <item name="thumbHeight">52dp</item> Loading
packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt +29 −1 Original line number Diff line number Diff line Loading @@ -18,9 +18,13 @@ package com.android.systemui.volume.dialog import android.app.Dialog import android.content.Context import android.graphics.PixelFormat import android.os.Bundle import android.view.MotionEvent import android.view.ViewGroup import android.view.WindowManager import com.android.systemui.dagger.qualifiers.Application 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.VolumeDialogViewBinder Loading @@ -32,10 +36,34 @@ constructor( @Application context: Context, private val viewBinder: VolumeDialogViewBinder, 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?) { super.onCreate(savedInstanceState) setContentView(R.layout.volume_dialog) viewBinder.bind(this) } Loading
packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt +56 −48 Original line number Diff line number Diff line Loading @@ -17,21 +17,22 @@ 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.graphics.Rect import android.graphics.Region import android.view.Gravity import android.view.View import android.view.ViewGroup import android.view.Window import android.view.WindowManager import android.view.ViewTreeObserver import android.view.ViewTreeObserver.InternalInsetsInfo import android.widget.FrameLayout import androidx.annotation.GravityInt 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.util.children 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 Loading @@ -52,6 +53,8 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.suspendCancellableCoroutine /** Binds the root view of the Volume Dialog. */ @OptIn(ExperimentalCoroutinesApi::class) Loading @@ -61,65 +64,41 @@ class VolumeDialogViewBinder constructor( private val volumeResources: VolumeDialogResources, private val gravityViewModel: VolumeDialogGravityViewModel, private val viewModelFactory: VolumeDialogViewModel.Factory, private val dialogViewModelFactory: 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) { setupDialog(dialog) val view: View = dialog.requireViewById(R.id.volume_dialog_container) view.alpha = 0f view.repeatWhenAttached { view.viewModel( // Root view of the Volume Dialog. val root: ViewGroup = dialog.requireViewById(R.id.volume_dialog_root) // Volume Dialog container view that contains the dialog itself without the floating sliders val container: View = root.requireViewById(R.id.volume_dialog_container) container.alpha = 0f container.repeatWhenAttached { root.viewModel( traceName = "VolumeDialogViewBinder", minWindowLifecycleState = WindowLifecycleState.ATTACHED, factory = { viewModelFactory.create() }, factory = { dialogViewModelFactory.create() }, ) { viewModel -> animateVisibility(container, dialog, viewModel.dialogVisibilityModel) 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() } } 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) volumeDialogRingerViewBinder.bind(root) slidersViewBinder.bind(root) settingsButtonViewBinder.bind(root) } private fun CoroutineScope.animateVisibility( Loading Loading @@ -209,4 +188,33 @@ constructor( } 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 } } }