Loading packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt +8 −1 Original line number Diff line number Diff line Loading @@ -133,7 +133,14 @@ fun SceneScope.QuickSettingsLayout( Column( verticalArrangement = Arrangement.spacedBy(QuickSettingsShade.Dimensions.Padding), horizontalAlignment = Alignment.CenterHorizontally, modifier = modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding), modifier = modifier .fillMaxWidth() .padding( start = QuickSettingsShade.Dimensions.Padding, end = QuickSettingsShade.Dimensions.Padding, top = QuickSettingsShade.Dimensions.Padding, ), ) { BrightnessSliderContainer( viewModel = viewModel.brightnessSliderViewModel, Loading packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt +60 −18 Original line number Diff line number Diff line Loading @@ -23,7 +23,9 @@ import android.graphics.Rect import android.os.Bundle import android.util.IndentingPrintWriter import android.view.LayoutInflater import android.view.MotionEvent import android.view.View import android.view.ViewConfiguration import android.view.ViewGroup import android.widget.FrameLayout import androidx.activity.OnBackPressedDispatcher Loading @@ -35,6 +37,7 @@ import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.togetherWith import androidx.compose.foundation.ScrollState import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer Loading @@ -43,6 +46,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect Loading Loading @@ -83,6 +87,7 @@ import com.android.systemui.Dumpable import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.dump.DumpManager import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.lifecycle.setSnapshotBinding import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager import com.android.systemui.media.controls.ui.view.MediaHost import com.android.systemui.media.dagger.MediaModule.QS_PANEL Loading Loading @@ -145,6 +150,7 @@ constructor( private val qqsVisible = MutableStateFlow(false) private val qqsPositionOnRoot = Rect() private val composeViewPositionOnScreen = Rect() private val scrollState = ScrollState(0) // Inside object for namespacing private val notificationScrimClippingParams = Loading Loading @@ -210,6 +216,9 @@ constructor( context, { notificationScrimClippingParams.isEnabled }, { notificationScrimClippingParams.params.top }, // Only allow scrolling when we are fully expanded. That way, we don't intercept // swipes in lockscreen (when somehow QS is receiving touches). { scrollState.canScrollForward && viewModel.expansionState.value.progress >= 1f }, ) frame.addView( composeView, Loading Loading @@ -488,12 +497,8 @@ constructor( private fun setListenerCollections() { lifecycleScope.launch { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { launch { // TODO // setListenerJob( // scrollListener, // // ) this@QSFragmentCompose.view?.setSnapshotBinding { scrollListener.value?.onQsPanelScrollChanged(scrollState.value) } launch { setListenerJob( Loading Loading @@ -528,6 +533,7 @@ constructor( viewModel.containerViewModel.quickQuickSettingsViewModel.squishinessViewModel .squishiness .collectAsStateWithLifecycle() Column(modifier = Modifier.sysuiResTag("quick_qs_panel")) { Box( modifier = Loading Loading @@ -591,7 +597,12 @@ constructor( modifier = Modifier.element(ElementKeys.QuickSettingsContent).fillMaxSize().weight(1f) ) { Column { DisposableEffect(Unit) { lifecycleScope.launch { scrollState.scrollTo(0) } onDispose { lifecycleScope.launch { scrollState.scrollTo(0) } } } Column(modifier = Modifier.verticalScroll(scrollState)) { Spacer( modifier = Modifier.height { qqsPadding + qsExtraPadding.roundToPx() } ) Loading @@ -601,18 +612,17 @@ constructor( ) } } } QuickSettingsTheme { FooterActions( viewModel = viewModel.footerActionsViewModel, qsVisibilityLifecycleOwner = this@QSFragmentCompose, modifier = Modifier.sysuiResTag("qs_footer_actions") .element(ElementKeys.FooterActions), Modifier.sysuiResTag("qs_footer_actions").element(ElementKeys.FooterActions), ) } } } } private fun Modifier.collapseExpandSemanticAction(label: String): Modifier { return viewModel.collapseExpandAccessibilityAction?.let { Loading Loading @@ -791,13 +801,17 @@ private class ExpansionTransition(currentProgress: Float) : private const val EDIT_MODE_TIME_MILLIS = 500 /** * Ignore touches below the value returned by [clippingTopProvider], when clipping is enabled, as * Performs different touch handling based on the state of the ComposeView: * * Ignore touches below the value returned by [clippingTopProvider], when clipping is enabled, as * per [clippingEnabledProvider]. * * Intercept touches that would overscroll QS forward and instead allow them to be used to close * the shade. */ private class FrameLayoutTouchPassthrough( context: Context, private val clippingEnabledProvider: () -> Boolean, private val clippingTopProvider: () -> Int, private val canScrollForwardQs: () -> Boolean, ) : FrameLayout(context) { override fun isTransformedTouchPointInView( x: Float, Loading @@ -811,4 +825,32 @@ private class FrameLayoutTouchPassthrough( super.isTransformedTouchPointInView(x, y, child, outLocalPoint) } } val touchSlop = ViewConfiguration.get(context).scaledTouchSlop var downY = 0f override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { // If there's a touch on this view and we can scroll down, we don't want to be intercepted val action = ev.actionMasked when (action) { MotionEvent.ACTION_DOWN -> { // If we can scroll down, make sure none of our parents intercepts us. if (canScrollForwardQs()) { parent?.requestDisallowInterceptTouchEvent(true) } downY = ev.y } MotionEvent.ACTION_MOVE -> { val y = ev.y.toInt() val yDiff: Float = y - downY if (yDiff < -touchSlop && !canScrollForwardQs()) { // Intercept touches that are overscrolling. return true } } } return super.onInterceptTouchEvent(ev) } } packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt +4 −2 Original line number Diff line number Diff line Loading @@ -19,7 +19,7 @@ package com.android.systemui.qs.panels.ui.compose import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.requiredHeight import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.material.icons.Icons Loading Loading @@ -97,7 +97,9 @@ constructor( TileGrid(tiles = page, modifier = Modifier, editModeStart = {}) } } Box(modifier = Modifier.height(FooterHeight).fillMaxWidth()) { // Use requiredHeight so it won't be squished if the view doesn't quite fit. As this is // expected to be inside a scrollable container, this should not be an issue. Box(modifier = Modifier.requiredHeight(FooterHeight).fillMaxWidth()) { PagerDots( pagerState = pagerState, activeColor = MaterialTheme.colorScheme.primary, Loading packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt +1 −2 Original line number Diff line number Diff line Loading @@ -41,8 +41,7 @@ fun SceneScope.QuickQuickSettings( viewModel: QuickQuickSettingsViewModel, modifier: Modifier = Modifier, ) { val sizedTiles by viewModel.tileViewModels.collectAsStateWithLifecycle(initialValue = emptyList()) val sizedTiles by viewModel.tileViewModels.collectAsStateWithLifecycle() val tiles = sizedTiles.fastMap { it.tile } val bounceables = remember(sizedTiles) { List(sizedTiles.size) { BounceableTileViewModel() } } val squishiness by viewModel.squishinessViewModel.squishiness.collectAsStateWithLifecycle() Loading Loading
packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt +8 −1 Original line number Diff line number Diff line Loading @@ -133,7 +133,14 @@ fun SceneScope.QuickSettingsLayout( Column( verticalArrangement = Arrangement.spacedBy(QuickSettingsShade.Dimensions.Padding), horizontalAlignment = Alignment.CenterHorizontally, modifier = modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding), modifier = modifier .fillMaxWidth() .padding( start = QuickSettingsShade.Dimensions.Padding, end = QuickSettingsShade.Dimensions.Padding, top = QuickSettingsShade.Dimensions.Padding, ), ) { BrightnessSliderContainer( viewModel = viewModel.brightnessSliderViewModel, Loading
packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt +60 −18 Original line number Diff line number Diff line Loading @@ -23,7 +23,9 @@ import android.graphics.Rect import android.os.Bundle import android.util.IndentingPrintWriter import android.view.LayoutInflater import android.view.MotionEvent import android.view.View import android.view.ViewConfiguration import android.view.ViewGroup import android.widget.FrameLayout import androidx.activity.OnBackPressedDispatcher Loading @@ -35,6 +37,7 @@ import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.togetherWith import androidx.compose.foundation.ScrollState import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer Loading @@ -43,6 +46,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect Loading Loading @@ -83,6 +87,7 @@ import com.android.systemui.Dumpable import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.dump.DumpManager import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.lifecycle.setSnapshotBinding import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager import com.android.systemui.media.controls.ui.view.MediaHost import com.android.systemui.media.dagger.MediaModule.QS_PANEL Loading Loading @@ -145,6 +150,7 @@ constructor( private val qqsVisible = MutableStateFlow(false) private val qqsPositionOnRoot = Rect() private val composeViewPositionOnScreen = Rect() private val scrollState = ScrollState(0) // Inside object for namespacing private val notificationScrimClippingParams = Loading Loading @@ -210,6 +216,9 @@ constructor( context, { notificationScrimClippingParams.isEnabled }, { notificationScrimClippingParams.params.top }, // Only allow scrolling when we are fully expanded. That way, we don't intercept // swipes in lockscreen (when somehow QS is receiving touches). { scrollState.canScrollForward && viewModel.expansionState.value.progress >= 1f }, ) frame.addView( composeView, Loading Loading @@ -488,12 +497,8 @@ constructor( private fun setListenerCollections() { lifecycleScope.launch { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { launch { // TODO // setListenerJob( // scrollListener, // // ) this@QSFragmentCompose.view?.setSnapshotBinding { scrollListener.value?.onQsPanelScrollChanged(scrollState.value) } launch { setListenerJob( Loading Loading @@ -528,6 +533,7 @@ constructor( viewModel.containerViewModel.quickQuickSettingsViewModel.squishinessViewModel .squishiness .collectAsStateWithLifecycle() Column(modifier = Modifier.sysuiResTag("quick_qs_panel")) { Box( modifier = Loading Loading @@ -591,7 +597,12 @@ constructor( modifier = Modifier.element(ElementKeys.QuickSettingsContent).fillMaxSize().weight(1f) ) { Column { DisposableEffect(Unit) { lifecycleScope.launch { scrollState.scrollTo(0) } onDispose { lifecycleScope.launch { scrollState.scrollTo(0) } } } Column(modifier = Modifier.verticalScroll(scrollState)) { Spacer( modifier = Modifier.height { qqsPadding + qsExtraPadding.roundToPx() } ) Loading @@ -601,18 +612,17 @@ constructor( ) } } } QuickSettingsTheme { FooterActions( viewModel = viewModel.footerActionsViewModel, qsVisibilityLifecycleOwner = this@QSFragmentCompose, modifier = Modifier.sysuiResTag("qs_footer_actions") .element(ElementKeys.FooterActions), Modifier.sysuiResTag("qs_footer_actions").element(ElementKeys.FooterActions), ) } } } } private fun Modifier.collapseExpandSemanticAction(label: String): Modifier { return viewModel.collapseExpandAccessibilityAction?.let { Loading Loading @@ -791,13 +801,17 @@ private class ExpansionTransition(currentProgress: Float) : private const val EDIT_MODE_TIME_MILLIS = 500 /** * Ignore touches below the value returned by [clippingTopProvider], when clipping is enabled, as * Performs different touch handling based on the state of the ComposeView: * * Ignore touches below the value returned by [clippingTopProvider], when clipping is enabled, as * per [clippingEnabledProvider]. * * Intercept touches that would overscroll QS forward and instead allow them to be used to close * the shade. */ private class FrameLayoutTouchPassthrough( context: Context, private val clippingEnabledProvider: () -> Boolean, private val clippingTopProvider: () -> Int, private val canScrollForwardQs: () -> Boolean, ) : FrameLayout(context) { override fun isTransformedTouchPointInView( x: Float, Loading @@ -811,4 +825,32 @@ private class FrameLayoutTouchPassthrough( super.isTransformedTouchPointInView(x, y, child, outLocalPoint) } } val touchSlop = ViewConfiguration.get(context).scaledTouchSlop var downY = 0f override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { // If there's a touch on this view and we can scroll down, we don't want to be intercepted val action = ev.actionMasked when (action) { MotionEvent.ACTION_DOWN -> { // If we can scroll down, make sure none of our parents intercepts us. if (canScrollForwardQs()) { parent?.requestDisallowInterceptTouchEvent(true) } downY = ev.y } MotionEvent.ACTION_MOVE -> { val y = ev.y.toInt() val yDiff: Float = y - downY if (yDiff < -touchSlop && !canScrollForwardQs()) { // Intercept touches that are overscrolling. return true } } } return super.onInterceptTouchEvent(ev) } }
packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt +4 −2 Original line number Diff line number Diff line Loading @@ -19,7 +19,7 @@ package com.android.systemui.qs.panels.ui.compose import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.requiredHeight import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.material.icons.Icons Loading Loading @@ -97,7 +97,9 @@ constructor( TileGrid(tiles = page, modifier = Modifier, editModeStart = {}) } } Box(modifier = Modifier.height(FooterHeight).fillMaxWidth()) { // Use requiredHeight so it won't be squished if the view doesn't quite fit. As this is // expected to be inside a scrollable container, this should not be an issue. Box(modifier = Modifier.requiredHeight(FooterHeight).fillMaxWidth()) { PagerDots( pagerState = pagerState, activeColor = MaterialTheme.colorScheme.primary, Loading
packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt +1 −2 Original line number Diff line number Diff line Loading @@ -41,8 +41,7 @@ fun SceneScope.QuickQuickSettings( viewModel: QuickQuickSettingsViewModel, modifier: Modifier = Modifier, ) { val sizedTiles by viewModel.tileViewModels.collectAsStateWithLifecycle(initialValue = emptyList()) val sizedTiles by viewModel.tileViewModels.collectAsStateWithLifecycle() val tiles = sizedTiles.fastMap { it.tile } val bounceables = remember(sizedTiles) { List(sizedTiles.size) { BounceableTileViewModel() } } val squishiness by viewModel.squishinessViewModel.squishiness.collectAsStateWithLifecycle() Loading