Loading packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt +95 −32 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.systemui.qs.composefragment import android.annotation.SuppressLint import android.graphics.Rect import android.os.Bundle import android.util.IndentingPrintWriter import android.view.LayoutInflater import android.view.View import android.view.ViewGroup Loading @@ -41,8 +42,8 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.layout.layout import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.layout.onPlaced import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.layout.positionInRoot import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.res.dimensionResource Loading @@ -59,7 +60,9 @@ import com.android.compose.modifiers.height import com.android.compose.modifiers.padding import com.android.compose.modifiers.thenIf import com.android.compose.theme.PlatformTheme 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.media.controls.ui.controller.MediaHierarchyManager import com.android.systemui.media.controls.ui.view.MediaHost Loading @@ -76,6 +79,10 @@ import com.android.systemui.qs.ui.composable.QuickSettingsTheme import com.android.systemui.qs.ui.composable.ShadeBody import com.android.systemui.res.R import com.android.systemui.util.LifecycleFragment import com.android.systemui.util.asIndenting import com.android.systemui.util.printSection import com.android.systemui.util.println import java.io.PrintWriter import java.util.function.Consumer import javax.inject.Inject import javax.inject.Named Loading @@ -91,9 +98,10 @@ class QSFragmentCompose @Inject constructor( private val qsFragmentComposeViewModelFactory: QSFragmentComposeViewModel.Factory, private val dumpManager: DumpManager, @Named(QUICK_QS_PANEL) private val qqsMediaHost: MediaHost, @Named(QS_PANEL) private val qsMediaHost: MediaHost, ) : LifecycleFragment(), QS { ) : LifecycleFragment(), QS, Dumpable { private val scrollListener = MutableStateFlow<QS.ScrollListener?>(null) private val heightListener = MutableStateFlow<QS.HeightListener?>(null) Loading @@ -118,6 +126,22 @@ constructor( var top by mutableStateOf(0) var bottom by mutableStateOf(0) var radius by mutableStateOf(0) fun dump(pw: IndentingPrintWriter) { pw.printSection("NotificationScrimClippingParams") { pw.println("isEnabled", isEnabled) pw.println("leftInset", "${leftInset}px") pw.println("rightInset", "${rightInset}px") pw.println("top", "${top}px") pw.println("bottom", "${bottom}px") pw.println("radius", "${radius}px") } } } override fun onStart() { super.onStart() registerDumpable() } override fun onCreate(savedInstanceState: Bundle?) { Loading Loading @@ -343,11 +367,11 @@ constructor( } override fun getHeaderTop(): Int { return viewModel.qqsHeaderHeight.value return qqsPositionOnRoot.top } override fun getHeaderBottom(): Int { return headerTop + qqsHeight.value return qqsPositionOnRoot.bottom } override fun getHeaderLeft(): Int { Loading @@ -358,7 +382,7 @@ constructor( outBounds.set(qqsPositionOnRoot) view?.getBoundsOnScreen(composeViewPositionOnScreen) ?: run { composeViewPositionOnScreen.setEmpty() } qqsPositionOnRoot.offset(composeViewPositionOnScreen.left, composeViewPositionOnScreen.top) outBounds.offset(composeViewPositionOnScreen.left, composeViewPositionOnScreen.top) } override fun isHeaderShown(): Boolean { Loading Loading @@ -404,36 +428,28 @@ constructor( onDispose { qqsVisible.value = false } } Column(modifier = Modifier.sysuiResTag("quick_qs_panel")) { Box(modifier = Modifier.fillMaxWidth()) { val qsEnabled by viewModel.qsEnabled.collectAsStateWithLifecycle() if (qsEnabled) { QuickQuickSettings( viewModel = viewModel.containerViewModel.quickQuickSettingsViewModel, Box( modifier = Modifier.onGloballyPositioned { coordinates -> val (leftFromRoot, topFromRoot) = coordinates.positionInRoot().round() val (width, height) = coordinates.size Modifier.fillMaxWidth() .onPlaced { coordinates -> val (leftFromRoot, topFromRoot) = coordinates.positionInRoot().round() qqsPositionOnRoot.set( leftFromRoot, topFromRoot, leftFromRoot + width, topFromRoot + height leftFromRoot + coordinates.size.width, topFromRoot + coordinates.size.height, ) } .layout { measurable, constraints -> val placeable = measurable.measure(constraints) qqsHeight.value = placeable.height layout(placeable.width, placeable.height) { placeable.place(0, 0) } } .onSizeChanged { size -> qqsHeight.value = size.height } .padding(top = { qqsPadding }) .collapseExpandSemanticAction( stringResource( id = R.string.accessibility_quick_settings_expand ) ) { val qsEnabled by viewModel.qsEnabled.collectAsStateWithLifecycle() if (qsEnabled) { QuickQuickSettings( viewModel = viewModel.containerViewModel.quickQuickSettingsViewModel, modifier = Modifier.collapseExpandSemanticAction( stringResource(id = R.string.accessibility_quick_settings_expand) ) ) } Loading Loading @@ -486,6 +502,44 @@ constructor( } } ?: this } private fun registerDumpable() { val instanceId = instanceProvider.getNextId() // Add an instanceId because the system may have more than 1 of these when re-inflating and // DumpManager doesn't like repeated identifiers. Also, put it first because DumpHandler // matches by end. val stringId = "$instanceId-QSFragmentCompose" lifecycleScope.launch { lifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) { try { dumpManager.registerNormalDumpable(stringId, this@QSFragmentCompose) awaitCancellation() } finally { dumpManager.unregisterDumpable(stringId) } } } } override fun dump(pw: PrintWriter, args: Array<out String>) { pw.asIndenting().run { notificationScrimClippingParams.dump(this) printSection("QQS positioning") { println("qqsHeight", "${headerHeight}px") println("qqsTop", "${headerTop}px") println("qqsBottom", "${headerBottom}px") println("qqsLeft", "${headerLeft}px") println("qqsPositionOnRoot", qqsPositionOnRoot) val rect = Rect() getHeaderBoundsOnScreen(rect) println("qqsPositionOnScreen", rect) } println("QQS visible", qqsVisible.value) if (::viewModel.isInitialized) { printSection("View Model") { viewModel.dump(this@run, args) } } } } } private fun View.setBackPressedDispatcher() { Loading Loading @@ -526,3 +580,12 @@ private suspend inline fun <Listener : Any, Data> setListenerJob( } } } private val instanceProvider = object { private var currentId = 0 fun getNextId(): Int { return currentId++ } } packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt +30 −1 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.graphics.Rect import androidx.annotation.FloatRange import androidx.annotation.VisibleForTesting import androidx.lifecycle.LifecycleCoroutineScope import com.android.systemui.Dumpable import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.statusbar.StatusBarStateController Loading @@ -34,10 +35,14 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.util.LargeScreenUtils import com.android.systemui.util.asIndenting import com.android.systemui.util.printSection import com.android.systemui.util.println import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import java.io.PrintWriter import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted Loading @@ -62,7 +67,7 @@ constructor( private val configurationInteractor: ConfigurationInteractor, private val largeScreenHeaderHelper: LargeScreenHeaderHelper, @Assisted private val lifecycleScope: LifecycleCoroutineScope, ) { ) : Dumpable { val footerActionsViewModel = footerActionsViewModelFactory.create(lifecycleScope).also { lifecycleScope.launch { footerActionsController.init() } Loading Loading @@ -228,6 +233,30 @@ constructor( */ var collapseExpandAccessibilityAction: Runnable? = null override fun dump(pw: PrintWriter, args: Array<out String>) { pw.asIndenting().run { printSection("Quick Settings state") { println("isQSExpanded", isQSExpanded) println("isQSVisible", isQSVisible) println("isQSEnabled", qsEnabled.value) println("isCustomizing", containerViewModel.editModeViewModel.isEditing.value) } printSection("Expansion state") { println("qsExpansion", qsExpansionValue) println("panelExpansionFraction", panelExpansionFractionValue) println("squishinessFraction", squishinessFractionValue) println("expansionState", expansionState.value) } printSection("Shade state") { println("stackOverscrolling", stackScrollerOverscrollingValue) println("statusBarState", StatusBarState.toString(statusBarState.value)) println("isSmallScreen", isSmallScreenValue) println("heightOverride", "${heightOverrideValue}px") println("qqsHeaderHeight", "${qqsHeaderHeight.value}px") } } } @AssistedFactory interface Factory { fun create(lifecycleScope: LifecycleCoroutineScope): QSFragmentComposeViewModel Loading Loading
packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt +95 −32 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.systemui.qs.composefragment import android.annotation.SuppressLint import android.graphics.Rect import android.os.Bundle import android.util.IndentingPrintWriter import android.view.LayoutInflater import android.view.View import android.view.ViewGroup Loading @@ -41,8 +42,8 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.layout.layout import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.layout.onPlaced import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.layout.positionInRoot import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.res.dimensionResource Loading @@ -59,7 +60,9 @@ import com.android.compose.modifiers.height import com.android.compose.modifiers.padding import com.android.compose.modifiers.thenIf import com.android.compose.theme.PlatformTheme 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.media.controls.ui.controller.MediaHierarchyManager import com.android.systemui.media.controls.ui.view.MediaHost Loading @@ -76,6 +79,10 @@ import com.android.systemui.qs.ui.composable.QuickSettingsTheme import com.android.systemui.qs.ui.composable.ShadeBody import com.android.systemui.res.R import com.android.systemui.util.LifecycleFragment import com.android.systemui.util.asIndenting import com.android.systemui.util.printSection import com.android.systemui.util.println import java.io.PrintWriter import java.util.function.Consumer import javax.inject.Inject import javax.inject.Named Loading @@ -91,9 +98,10 @@ class QSFragmentCompose @Inject constructor( private val qsFragmentComposeViewModelFactory: QSFragmentComposeViewModel.Factory, private val dumpManager: DumpManager, @Named(QUICK_QS_PANEL) private val qqsMediaHost: MediaHost, @Named(QS_PANEL) private val qsMediaHost: MediaHost, ) : LifecycleFragment(), QS { ) : LifecycleFragment(), QS, Dumpable { private val scrollListener = MutableStateFlow<QS.ScrollListener?>(null) private val heightListener = MutableStateFlow<QS.HeightListener?>(null) Loading @@ -118,6 +126,22 @@ constructor( var top by mutableStateOf(0) var bottom by mutableStateOf(0) var radius by mutableStateOf(0) fun dump(pw: IndentingPrintWriter) { pw.printSection("NotificationScrimClippingParams") { pw.println("isEnabled", isEnabled) pw.println("leftInset", "${leftInset}px") pw.println("rightInset", "${rightInset}px") pw.println("top", "${top}px") pw.println("bottom", "${bottom}px") pw.println("radius", "${radius}px") } } } override fun onStart() { super.onStart() registerDumpable() } override fun onCreate(savedInstanceState: Bundle?) { Loading Loading @@ -343,11 +367,11 @@ constructor( } override fun getHeaderTop(): Int { return viewModel.qqsHeaderHeight.value return qqsPositionOnRoot.top } override fun getHeaderBottom(): Int { return headerTop + qqsHeight.value return qqsPositionOnRoot.bottom } override fun getHeaderLeft(): Int { Loading @@ -358,7 +382,7 @@ constructor( outBounds.set(qqsPositionOnRoot) view?.getBoundsOnScreen(composeViewPositionOnScreen) ?: run { composeViewPositionOnScreen.setEmpty() } qqsPositionOnRoot.offset(composeViewPositionOnScreen.left, composeViewPositionOnScreen.top) outBounds.offset(composeViewPositionOnScreen.left, composeViewPositionOnScreen.top) } override fun isHeaderShown(): Boolean { Loading Loading @@ -404,36 +428,28 @@ constructor( onDispose { qqsVisible.value = false } } Column(modifier = Modifier.sysuiResTag("quick_qs_panel")) { Box(modifier = Modifier.fillMaxWidth()) { val qsEnabled by viewModel.qsEnabled.collectAsStateWithLifecycle() if (qsEnabled) { QuickQuickSettings( viewModel = viewModel.containerViewModel.quickQuickSettingsViewModel, Box( modifier = Modifier.onGloballyPositioned { coordinates -> val (leftFromRoot, topFromRoot) = coordinates.positionInRoot().round() val (width, height) = coordinates.size Modifier.fillMaxWidth() .onPlaced { coordinates -> val (leftFromRoot, topFromRoot) = coordinates.positionInRoot().round() qqsPositionOnRoot.set( leftFromRoot, topFromRoot, leftFromRoot + width, topFromRoot + height leftFromRoot + coordinates.size.width, topFromRoot + coordinates.size.height, ) } .layout { measurable, constraints -> val placeable = measurable.measure(constraints) qqsHeight.value = placeable.height layout(placeable.width, placeable.height) { placeable.place(0, 0) } } .onSizeChanged { size -> qqsHeight.value = size.height } .padding(top = { qqsPadding }) .collapseExpandSemanticAction( stringResource( id = R.string.accessibility_quick_settings_expand ) ) { val qsEnabled by viewModel.qsEnabled.collectAsStateWithLifecycle() if (qsEnabled) { QuickQuickSettings( viewModel = viewModel.containerViewModel.quickQuickSettingsViewModel, modifier = Modifier.collapseExpandSemanticAction( stringResource(id = R.string.accessibility_quick_settings_expand) ) ) } Loading Loading @@ -486,6 +502,44 @@ constructor( } } ?: this } private fun registerDumpable() { val instanceId = instanceProvider.getNextId() // Add an instanceId because the system may have more than 1 of these when re-inflating and // DumpManager doesn't like repeated identifiers. Also, put it first because DumpHandler // matches by end. val stringId = "$instanceId-QSFragmentCompose" lifecycleScope.launch { lifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) { try { dumpManager.registerNormalDumpable(stringId, this@QSFragmentCompose) awaitCancellation() } finally { dumpManager.unregisterDumpable(stringId) } } } } override fun dump(pw: PrintWriter, args: Array<out String>) { pw.asIndenting().run { notificationScrimClippingParams.dump(this) printSection("QQS positioning") { println("qqsHeight", "${headerHeight}px") println("qqsTop", "${headerTop}px") println("qqsBottom", "${headerBottom}px") println("qqsLeft", "${headerLeft}px") println("qqsPositionOnRoot", qqsPositionOnRoot) val rect = Rect() getHeaderBoundsOnScreen(rect) println("qqsPositionOnScreen", rect) } println("QQS visible", qqsVisible.value) if (::viewModel.isInitialized) { printSection("View Model") { viewModel.dump(this@run, args) } } } } } private fun View.setBackPressedDispatcher() { Loading Loading @@ -526,3 +580,12 @@ private suspend inline fun <Listener : Any, Data> setListenerJob( } } } private val instanceProvider = object { private var currentId = 0 fun getNextId(): Int { return currentId++ } }
packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt +30 −1 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.graphics.Rect import androidx.annotation.FloatRange import androidx.annotation.VisibleForTesting import androidx.lifecycle.LifecycleCoroutineScope import com.android.systemui.Dumpable import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.statusbar.StatusBarStateController Loading @@ -34,10 +35,14 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.util.LargeScreenUtils import com.android.systemui.util.asIndenting import com.android.systemui.util.printSection import com.android.systemui.util.println import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import java.io.PrintWriter import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted Loading @@ -62,7 +67,7 @@ constructor( private val configurationInteractor: ConfigurationInteractor, private val largeScreenHeaderHelper: LargeScreenHeaderHelper, @Assisted private val lifecycleScope: LifecycleCoroutineScope, ) { ) : Dumpable { val footerActionsViewModel = footerActionsViewModelFactory.create(lifecycleScope).also { lifecycleScope.launch { footerActionsController.init() } Loading Loading @@ -228,6 +233,30 @@ constructor( */ var collapseExpandAccessibilityAction: Runnable? = null override fun dump(pw: PrintWriter, args: Array<out String>) { pw.asIndenting().run { printSection("Quick Settings state") { println("isQSExpanded", isQSExpanded) println("isQSVisible", isQSVisible) println("isQSEnabled", qsEnabled.value) println("isCustomizing", containerViewModel.editModeViewModel.isEditing.value) } printSection("Expansion state") { println("qsExpansion", qsExpansionValue) println("panelExpansionFraction", panelExpansionFractionValue) println("squishinessFraction", squishinessFractionValue) println("expansionState", expansionState.value) } printSection("Shade state") { println("stackOverscrolling", stackScrollerOverscrollingValue) println("statusBarState", StatusBarState.toString(statusBarState.value)) println("isSmallScreen", isSmallScreenValue) println("heightOverride", "${heightOverrideValue}px") println("qqsHeaderHeight", "${qqsHeaderHeight.value}px") } } } @AssistedFactory interface Factory { fun create(lifecycleScope: LifecycleCoroutineScope): QSFragmentComposeViewModel Loading