Loading packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt +21 −18 Original line number Diff line number Diff line Loading @@ -132,6 +132,7 @@ import com.android.systemui.communal.ui.compose.extensions.observeTaps import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel import com.android.systemui.communal.ui.viewmodel.CommunalViewModel import com.android.systemui.communal.ui.viewmodel.PopupType import com.android.systemui.communal.widgets.WidgetConfigurator import com.android.systemui.res.R import com.android.systemui.statusbar.phone.SystemUIDialogFactory Loading @@ -148,8 +149,7 @@ fun CommunalHub( onEditDone: (() -> Unit)? = null, ) { val communalContent by viewModel.communalContent.collectAsState(initial = emptyList()) val isPopupOnDismissCtaShowing by viewModel.isPopupOnDismissCtaShowing.collectAsState(initial = false) val currentPopup by viewModel.currentPopup.collectAsState(initial = null) var removeButtonCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) } var toolbarSize: IntSize? by remember { mutableStateOf(null) } var gridCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) } Loading @@ -161,7 +161,6 @@ fun CommunalHub( val removeButtonEnabled by remember { derivedStateOf { selectedKey.value != null || reorderingWidgets } } var isButtonToEditWidgetsShowing by remember { mutableStateOf(false) } val isEmptyState by viewModel.isEmptyState.collectAsState(initial = false) val contentPadding = gridContentPadding(viewModel.isEditMode, toolbarSize) Loading Loading @@ -214,7 +213,7 @@ fun CommunalHub( communalContent[index] is CommunalContentModel.CtaTileInViewMode ) { isButtonToEditWidgetsShowing = true viewModel.onShowCustomizeWidgetButton() } val key = index?.let { keyAtIndexIfEditable(communalContent, index) } Loading Loading @@ -290,19 +289,23 @@ fun CommunalHub( ) } if (isPopupOnDismissCtaShowing) { PopupOnDismissCtaTile(viewModel::onHidePopupAfterDismissCta) if (currentPopup != null) { when (currentPopup) { PopupType.CtaTile -> { PopupOnDismissCtaTile(viewModel::onHidePopup) } if (isButtonToEditWidgetsShowing) { PopupType.CustomizeWidgetButton -> { ButtonToEditWidgets( onClick = { isButtonToEditWidgetsShowing = false viewModel.onHidePopup() viewModel.onOpenWidgetEditor(selectedKey.value) }, onHide = { isButtonToEditWidgetsShowing = false }, onHide = { viewModel.onHidePopup()} ) } null -> {} } } if (viewModel is CommunalViewModel && dialogFactory != null) { val isEnableWidgetDialogShowing by Loading Loading @@ -679,11 +682,11 @@ private fun ButtonToEditWidgets( } @Composable private fun PopupOnDismissCtaTile(onHidePopupAfterDismissCta: () -> Unit) { private fun PopupOnDismissCtaTile(onHidePopup: () -> Unit) { Popup( alignment = Alignment.TopCenter, offset = IntOffset(0, 40), onDismissRequest = onHidePopupAfterDismissCta onDismissRequest = onHidePopup ) { val colors = LocalAndroidColorScheme.current Row( Loading packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt +34 −7 Original line number Diff line number Diff line Loading @@ -38,6 +38,7 @@ import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.communal.shared.model.CommunalWidgetContentModel import com.android.systemui.communal.ui.viewmodel.CommunalViewModel import com.android.systemui.communal.ui.viewmodel.CommunalViewModel.Companion.POPUP_AUTO_HIDE_TIMEOUT_MS import com.android.systemui.communal.ui.viewmodel.PopupType import com.android.systemui.coroutines.collectLastValue import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED Loading Loading @@ -247,7 +248,7 @@ class CommunalViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() { tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED) val communalContent by collectLastValue(underTest.communalContent) val isPopupOnDismissCtaShowing by collectLastValue(underTest.isPopupOnDismissCtaShowing) val currentPopup by collectLastValue(underTest.currentPopup) assertThat(communalContent?.size).isEqualTo(1) assertThat(communalContent?.get(0)) Loading @@ -257,11 +258,11 @@ class CommunalViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() { // hide CTA tile and show the popup assertThat(communalContent).isEmpty() assertThat(isPopupOnDismissCtaShowing).isEqualTo(true) assertThat(currentPopup).isEqualTo(PopupType.CtaTile) // hide popup after time elapsed advanceTimeBy(POPUP_AUTO_HIDE_TIMEOUT_MS) assertThat(isPopupOnDismissCtaShowing).isEqualTo(false) assertThat(currentPopup).isNull() } @Test Loading @@ -269,14 +270,40 @@ class CommunalViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() { testScope.runTest { tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED) val isPopupOnDismissCtaShowing by collectLastValue(underTest.isPopupOnDismissCtaShowing) val currentPopup by collectLastValue(underTest.currentPopup) underTest.onDismissCtaTile() assertThat(isPopupOnDismissCtaShowing).isEqualTo(true) assertThat(currentPopup).isEqualTo(PopupType.CtaTile) // dismiss the popup directly underTest.onHidePopupAfterDismissCta() assertThat(isPopupOnDismissCtaShowing).isEqualTo(false) underTest.onHidePopup() assertThat(currentPopup).isNull() } @Test fun customizeWidgetButton_showsThenHidesAfterTimeout() = testScope.runTest { tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED) val currentPopup by collectLastValue(underTest.currentPopup) assertThat(currentPopup).isNull() underTest.onShowCustomizeWidgetButton() assertThat(currentPopup).isEqualTo(PopupType.CustomizeWidgetButton) advanceTimeBy(POPUP_AUTO_HIDE_TIMEOUT_MS) assertThat(currentPopup).isNull() } @Test fun customizeWidgetButton_onDismiss_hidesImmediately() = testScope.runTest { tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED) val currentPopup by collectLastValue(underTest.currentPopup) underTest.onShowCustomizeWidgetButton() assertThat(currentPopup).isEqualTo(PopupType.CustomizeWidgetButton) underTest.onHidePopup() assertThat(currentPopup).isNull() } @Test Loading packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt +7 −4 Original line number Diff line number Diff line Loading @@ -88,14 +88,14 @@ abstract class BaseCommunalViewModel( /** Whether in edit mode for the communal hub. */ open val isEditMode = false /** Whether the popup message triggered by dismissing the CTA tile is showing. */ open val isPopupOnDismissCtaShowing: Flow<Boolean> = flowOf(false) /** Whether the type of popup currently showing */ open val currentPopup: Flow<PopupType?> = flowOf(null) /** Whether the communal hub is empty with no widget available. */ open val isEmptyState: Flow<Boolean> = flowOf(false) /** Hide the popup message triggered by dismissing the CTA tile. */ open fun onHidePopupAfterDismissCta() {} /** Called as the UI request to dismiss the any displaying popup */ open fun onHidePopup() {} /** Called as the UI requests deleting a widget. */ open fun onDeleteWidget(id: Int) {} Loading Loading @@ -127,6 +127,9 @@ abstract class BaseCommunalViewModel( /** Called as the user cancels dragging a widget to reorder. */ open fun onReorderWidgetCancel() {} /** Called as the user request to show the customize widget button. */ open fun onShowCustomizeWidgetButton() {} /** Set the key of the currently selected item */ fun setSelectedKey(key: String?) { _selectedKey.value = key Loading packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt +27 −24 Original line number Diff line number Diff line Loading @@ -90,9 +90,8 @@ constructor( .distinctUntilChanged() .onEach { logger.d("isEmptyState: $it") } private val _isPopupOnDismissCtaShowing: MutableStateFlow<Boolean> = MutableStateFlow(false) override val isPopupOnDismissCtaShowing: Flow<Boolean> = _isPopupOnDismissCtaShowing.asStateFlow() private val _currentPopup: MutableStateFlow<PopupType?> = MutableStateFlow(null) override val currentPopup: Flow<PopupType?> = _currentPopup.asStateFlow() private val _isEnableWidgetDialogShowing: MutableStateFlow<Boolean> = MutableStateFlow(false) val isEnableWidgetDialogShowing: Flow<Boolean> = _isEnableWidgetDialogShowing.asStateFlow() Loading Loading @@ -124,14 +123,16 @@ constructor( override fun onDismissCtaTile() { scope.launch { communalInteractor.dismissCtaTile() setPopupOnDismissCtaVisibility(true) schedulePopupHiding() setCurrentPopupType(PopupType.CtaTile) } } override fun onHidePopupAfterDismissCta() { cancelDelayedPopupHiding() setPopupOnDismissCtaVisibility(false) override fun onShowCustomizeWidgetButton() { setCurrentPopupType(PopupType.CustomizeWidgetButton) } override fun onHidePopup() { setCurrentPopupType(null) } override fun onOpenEnableWidgetDialog() { Loading Loading @@ -168,26 +169,23 @@ constructor( _isEnableWorkProfileDialogShowing.value = isVisible } private fun setPopupOnDismissCtaVisibility(isVisible: Boolean) { _isPopupOnDismissCtaShowing.value = isVisible } private var delayedHidePopupJob: Job? = null private fun setCurrentPopupType(popupType: PopupType?) { _currentPopup.value = popupType delayedHideCurrentPopupJob?.cancel() private fun schedulePopupHiding() { cancelDelayedPopupHiding() delayedHidePopupJob = if (popupType != null) { delayedHideCurrentPopupJob = scope.launch { delay(POPUP_AUTO_HIDE_TIMEOUT_MS) onHidePopupAfterDismissCta() setCurrentPopupType(null) } } else { delayedHideCurrentPopupJob = null } private fun cancelDelayedPopupHiding() { delayedHidePopupJob?.cancel() delayedHidePopupJob = null } private var delayedHideCurrentPopupJob: Job? = null /** Whether we can transition to a new scene based on a user gesture. */ fun canChangeScene(): Boolean { return !shadeInteractor.isAnyFullyExpanded.value Loading @@ -197,3 +195,8 @@ constructor( const val POPUP_AUTO_HIDE_TIMEOUT_MS = 12000L } } sealed class PopupType { object CtaTile : PopupType() object CustomizeWidgetButton : PopupType() } Loading
packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt +21 −18 Original line number Diff line number Diff line Loading @@ -132,6 +132,7 @@ import com.android.systemui.communal.ui.compose.extensions.observeTaps import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel import com.android.systemui.communal.ui.viewmodel.CommunalViewModel import com.android.systemui.communal.ui.viewmodel.PopupType import com.android.systemui.communal.widgets.WidgetConfigurator import com.android.systemui.res.R import com.android.systemui.statusbar.phone.SystemUIDialogFactory Loading @@ -148,8 +149,7 @@ fun CommunalHub( onEditDone: (() -> Unit)? = null, ) { val communalContent by viewModel.communalContent.collectAsState(initial = emptyList()) val isPopupOnDismissCtaShowing by viewModel.isPopupOnDismissCtaShowing.collectAsState(initial = false) val currentPopup by viewModel.currentPopup.collectAsState(initial = null) var removeButtonCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) } var toolbarSize: IntSize? by remember { mutableStateOf(null) } var gridCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) } Loading @@ -161,7 +161,6 @@ fun CommunalHub( val removeButtonEnabled by remember { derivedStateOf { selectedKey.value != null || reorderingWidgets } } var isButtonToEditWidgetsShowing by remember { mutableStateOf(false) } val isEmptyState by viewModel.isEmptyState.collectAsState(initial = false) val contentPadding = gridContentPadding(viewModel.isEditMode, toolbarSize) Loading Loading @@ -214,7 +213,7 @@ fun CommunalHub( communalContent[index] is CommunalContentModel.CtaTileInViewMode ) { isButtonToEditWidgetsShowing = true viewModel.onShowCustomizeWidgetButton() } val key = index?.let { keyAtIndexIfEditable(communalContent, index) } Loading Loading @@ -290,19 +289,23 @@ fun CommunalHub( ) } if (isPopupOnDismissCtaShowing) { PopupOnDismissCtaTile(viewModel::onHidePopupAfterDismissCta) if (currentPopup != null) { when (currentPopup) { PopupType.CtaTile -> { PopupOnDismissCtaTile(viewModel::onHidePopup) } if (isButtonToEditWidgetsShowing) { PopupType.CustomizeWidgetButton -> { ButtonToEditWidgets( onClick = { isButtonToEditWidgetsShowing = false viewModel.onHidePopup() viewModel.onOpenWidgetEditor(selectedKey.value) }, onHide = { isButtonToEditWidgetsShowing = false }, onHide = { viewModel.onHidePopup()} ) } null -> {} } } if (viewModel is CommunalViewModel && dialogFactory != null) { val isEnableWidgetDialogShowing by Loading Loading @@ -679,11 +682,11 @@ private fun ButtonToEditWidgets( } @Composable private fun PopupOnDismissCtaTile(onHidePopupAfterDismissCta: () -> Unit) { private fun PopupOnDismissCtaTile(onHidePopup: () -> Unit) { Popup( alignment = Alignment.TopCenter, offset = IntOffset(0, 40), onDismissRequest = onHidePopupAfterDismissCta onDismissRequest = onHidePopup ) { val colors = LocalAndroidColorScheme.current Row( Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt +34 −7 Original line number Diff line number Diff line Loading @@ -38,6 +38,7 @@ import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.communal.shared.model.CommunalWidgetContentModel import com.android.systemui.communal.ui.viewmodel.CommunalViewModel import com.android.systemui.communal.ui.viewmodel.CommunalViewModel.Companion.POPUP_AUTO_HIDE_TIMEOUT_MS import com.android.systemui.communal.ui.viewmodel.PopupType import com.android.systemui.coroutines.collectLastValue import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED Loading Loading @@ -247,7 +248,7 @@ class CommunalViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() { tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED) val communalContent by collectLastValue(underTest.communalContent) val isPopupOnDismissCtaShowing by collectLastValue(underTest.isPopupOnDismissCtaShowing) val currentPopup by collectLastValue(underTest.currentPopup) assertThat(communalContent?.size).isEqualTo(1) assertThat(communalContent?.get(0)) Loading @@ -257,11 +258,11 @@ class CommunalViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() { // hide CTA tile and show the popup assertThat(communalContent).isEmpty() assertThat(isPopupOnDismissCtaShowing).isEqualTo(true) assertThat(currentPopup).isEqualTo(PopupType.CtaTile) // hide popup after time elapsed advanceTimeBy(POPUP_AUTO_HIDE_TIMEOUT_MS) assertThat(isPopupOnDismissCtaShowing).isEqualTo(false) assertThat(currentPopup).isNull() } @Test Loading @@ -269,14 +270,40 @@ class CommunalViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() { testScope.runTest { tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED) val isPopupOnDismissCtaShowing by collectLastValue(underTest.isPopupOnDismissCtaShowing) val currentPopup by collectLastValue(underTest.currentPopup) underTest.onDismissCtaTile() assertThat(isPopupOnDismissCtaShowing).isEqualTo(true) assertThat(currentPopup).isEqualTo(PopupType.CtaTile) // dismiss the popup directly underTest.onHidePopupAfterDismissCta() assertThat(isPopupOnDismissCtaShowing).isEqualTo(false) underTest.onHidePopup() assertThat(currentPopup).isNull() } @Test fun customizeWidgetButton_showsThenHidesAfterTimeout() = testScope.runTest { tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED) val currentPopup by collectLastValue(underTest.currentPopup) assertThat(currentPopup).isNull() underTest.onShowCustomizeWidgetButton() assertThat(currentPopup).isEqualTo(PopupType.CustomizeWidgetButton) advanceTimeBy(POPUP_AUTO_HIDE_TIMEOUT_MS) assertThat(currentPopup).isNull() } @Test fun customizeWidgetButton_onDismiss_hidesImmediately() = testScope.runTest { tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED) val currentPopup by collectLastValue(underTest.currentPopup) underTest.onShowCustomizeWidgetButton() assertThat(currentPopup).isEqualTo(PopupType.CustomizeWidgetButton) underTest.onHidePopup() assertThat(currentPopup).isNull() } @Test Loading
packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt +7 −4 Original line number Diff line number Diff line Loading @@ -88,14 +88,14 @@ abstract class BaseCommunalViewModel( /** Whether in edit mode for the communal hub. */ open val isEditMode = false /** Whether the popup message triggered by dismissing the CTA tile is showing. */ open val isPopupOnDismissCtaShowing: Flow<Boolean> = flowOf(false) /** Whether the type of popup currently showing */ open val currentPopup: Flow<PopupType?> = flowOf(null) /** Whether the communal hub is empty with no widget available. */ open val isEmptyState: Flow<Boolean> = flowOf(false) /** Hide the popup message triggered by dismissing the CTA tile. */ open fun onHidePopupAfterDismissCta() {} /** Called as the UI request to dismiss the any displaying popup */ open fun onHidePopup() {} /** Called as the UI requests deleting a widget. */ open fun onDeleteWidget(id: Int) {} Loading Loading @@ -127,6 +127,9 @@ abstract class BaseCommunalViewModel( /** Called as the user cancels dragging a widget to reorder. */ open fun onReorderWidgetCancel() {} /** Called as the user request to show the customize widget button. */ open fun onShowCustomizeWidgetButton() {} /** Set the key of the currently selected item */ fun setSelectedKey(key: String?) { _selectedKey.value = key Loading
packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt +27 −24 Original line number Diff line number Diff line Loading @@ -90,9 +90,8 @@ constructor( .distinctUntilChanged() .onEach { logger.d("isEmptyState: $it") } private val _isPopupOnDismissCtaShowing: MutableStateFlow<Boolean> = MutableStateFlow(false) override val isPopupOnDismissCtaShowing: Flow<Boolean> = _isPopupOnDismissCtaShowing.asStateFlow() private val _currentPopup: MutableStateFlow<PopupType?> = MutableStateFlow(null) override val currentPopup: Flow<PopupType?> = _currentPopup.asStateFlow() private val _isEnableWidgetDialogShowing: MutableStateFlow<Boolean> = MutableStateFlow(false) val isEnableWidgetDialogShowing: Flow<Boolean> = _isEnableWidgetDialogShowing.asStateFlow() Loading Loading @@ -124,14 +123,16 @@ constructor( override fun onDismissCtaTile() { scope.launch { communalInteractor.dismissCtaTile() setPopupOnDismissCtaVisibility(true) schedulePopupHiding() setCurrentPopupType(PopupType.CtaTile) } } override fun onHidePopupAfterDismissCta() { cancelDelayedPopupHiding() setPopupOnDismissCtaVisibility(false) override fun onShowCustomizeWidgetButton() { setCurrentPopupType(PopupType.CustomizeWidgetButton) } override fun onHidePopup() { setCurrentPopupType(null) } override fun onOpenEnableWidgetDialog() { Loading Loading @@ -168,26 +169,23 @@ constructor( _isEnableWorkProfileDialogShowing.value = isVisible } private fun setPopupOnDismissCtaVisibility(isVisible: Boolean) { _isPopupOnDismissCtaShowing.value = isVisible } private var delayedHidePopupJob: Job? = null private fun setCurrentPopupType(popupType: PopupType?) { _currentPopup.value = popupType delayedHideCurrentPopupJob?.cancel() private fun schedulePopupHiding() { cancelDelayedPopupHiding() delayedHidePopupJob = if (popupType != null) { delayedHideCurrentPopupJob = scope.launch { delay(POPUP_AUTO_HIDE_TIMEOUT_MS) onHidePopupAfterDismissCta() setCurrentPopupType(null) } } else { delayedHideCurrentPopupJob = null } private fun cancelDelayedPopupHiding() { delayedHidePopupJob?.cancel() delayedHidePopupJob = null } private var delayedHideCurrentPopupJob: Job? = null /** Whether we can transition to a new scene based on a user gesture. */ fun canChangeScene(): Boolean { return !shadeInteractor.isAnyFullyExpanded.value Loading @@ -197,3 +195,8 @@ constructor( const val POPUP_AUTO_HIDE_TIMEOUT_MS = 12000L } } sealed class PopupType { object CtaTile : PopupType() object CustomizeWidgetButton : PopupType() }