diff options
| author | 2025-02-06 12:22:13 -0800 | |
|---|---|---|
| committer | 2025-02-06 12:22:13 -0800 | |
| commit | 1253632dc083fa1b092935627abf5b0932211ab0 (patch) | |
| tree | ba756af147c89de54eeb77e08a3aae85b2dabf73 | |
| parent | 7272c9605f197329c6fc7994cf78100c7e21821d (diff) | |
| parent | 98278c5d57953828296b4405af92bcebe611601a (diff) | |
Merge "Animate the "go to dream" button tooltip." into main
3 files changed, 77 insertions, 9 deletions
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt index a840a6f0476f..acaf43a62f43 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt @@ -16,6 +16,12 @@ package com.android.systemui.communal.ui.compose.section +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.MutableTransitionState +import androidx.compose.animation.expandVertically +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.shrinkVertically import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height @@ -28,6 +34,11 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.CornerRadius @@ -52,6 +63,8 @@ import com.android.systemui.communal.ui.viewmodel.CommunalToDreamButtonViewModel import com.android.systemui.lifecycle.rememberViewModel import com.android.systemui.res.R import javax.inject.Inject +import kotlin.time.Duration.Companion.seconds +import kotlinx.coroutines.delay class CommunalToDreamButtonSection @Inject @@ -75,16 +88,54 @@ constructor( val buttonSize = dimensionResource(R.dimen.communal_to_dream_button_size) if (viewModel.shouldShowTooltip) { + val tooltipVisibleState = remember { MutableTransitionState(false) } + Column( modifier = Modifier.widthIn(max = tooltipMaxWidth).pointerInput(Unit) { - observeTaps { viewModel.setDreamButtonTooltipDismissed() } + observeTaps { + if (tooltipVisibleState.isCurrentlyVisible()) { + tooltipVisibleState.targetState = false + } + } } ) { - Tooltip( - pointerOffsetDp = buttonSize.div(2), - text = stringResource(R.string.glanceable_hub_to_dream_button_tooltip), - ) + var waitingToShowTooltip by remember { mutableStateOf(true) } + + LaunchedEffect(tooltipVisibleState.targetState) { + delay(3.seconds) + tooltipVisibleState.targetState = true + waitingToShowTooltip = false + } + + // This LaunchedEffect is used to wait for the tooltip dismiss animation to + // complete before setting the tooltip dismissed. Otherwise, the composable would + // be removed before the animation can start. + LaunchedEffect( + tooltipVisibleState.currentState, + tooltipVisibleState.isIdle, + waitingToShowTooltip, + ) { + if ( + !waitingToShowTooltip && + !tooltipVisibleState.currentState && + tooltipVisibleState.isIdle + ) { + viewModel.setDreamButtonTooltipDismissed() + } + } + + AnimatedVisibility( + visibleState = tooltipVisibleState, + enter = fadeIn() + expandVertically(expandFrom = Alignment.Bottom), + exit = fadeOut() + shrinkVertically(shrinkTowards = Alignment.Bottom), + ) { + Tooltip( + pointerOffsetDp = buttonSize.div(2), + text = stringResource(R.string.glanceable_hub_to_dream_button_tooltip), + ) + } + GoToDreamButton( modifier = Modifier.width(buttonSize).height(buttonSize).align(Alignment.End) ) { @@ -98,6 +149,8 @@ constructor( } } + private fun MutableTransitionState<Boolean>.isCurrentlyVisible() = currentState && isIdle + companion object { private val tooltipMaxWidth = 350.dp } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt index b747705fa3a2..9fab652e2375 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt @@ -126,13 +126,23 @@ class CommunalToDreamButtonViewModelTest : SysuiTestCase() { } @Test - fun shouldShowDreamButtonTooltip_trueWhenNotDismissed() = + fun shouldShowDreamButtonTooltip_trueWhenNotDismissedAndHubOnboardingDismissed() = kosmos.runTest { + setSelectedUser(MAIN_USER) + fakeCommunalPrefsRepository.setHubOnboardingDismissed(MAIN_USER) runCurrent() + assertThat(underTest.shouldShowTooltip).isTrue() } @Test + fun shouldShowDreamButtonTooltip_falseWhenNotDismissedAndHubOnboardingNotDismissed() = + kosmos.runTest { + runCurrent() + assertThat(underTest.shouldShowTooltip).isFalse() + } + + @Test fun shouldShowDreamButtonTooltip_falseWhenDismissed() = kosmos.runTest { setSelectedUser(MAIN_USER) diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt index 7e683c45e525..b531d159acde 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt @@ -30,6 +30,8 @@ import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.lifecycle.Hydrator import com.android.systemui.plugins.ActivityStarter import com.android.systemui.statusbar.policy.BatteryController +import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf +import com.android.systemui.util.kotlin.BooleanFlowOperators.not import com.android.systemui.util.kotlin.isDevicePluggedIn import com.android.systemui.util.kotlin.sample import dagger.assisted.AssistedFactory @@ -40,7 +42,6 @@ import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -68,12 +69,16 @@ constructor( source = batteryController.isDevicePluggedIn().distinctUntilChanged(), ) - /** Return whether the dream button tooltip has been dismissed. */ + /** Return whether to show the dream button tooltip. */ val shouldShowTooltip: Boolean by hydrator.hydratedStateOf( traceName = "shouldShowTooltip", initialValue = false, - source = prefsInteractor.isDreamButtonTooltipDismissed.map { !it }, + source = + allOf( + not(prefsInteractor.isDreamButtonTooltipDismissed), + prefsInteractor.isHubOnboardingDismissed, + ), ) /** Set the dream button tooltip to be dismissed. */ |