Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit af43a5d6 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Instrument Ambient Cue CUJ. It monitors show/hide/expand/collapse...

Merge "Instrument Ambient Cue CUJ. It monitors show/hide/expand/collapse animation of cue bar." into main
parents ba54d011 59f9117b
Loading
Loading
Loading
Loading
+49 −1
Original line number Diff line number Diff line
@@ -476,8 +476,44 @@ public class Cuj {
     */
    public static final int CUJ_DESKTOP_MODE_MOVE_FROM_SPLIT_SCREEN = 144;

    /**
     * Tracks show animation for Cue bar. Cue bar is a floating bar that appears on the screen to
     * provide personalized contextual actions based on the current app.
     *
     * <p>Tracking begins when the cue bar UI starts to show and ends when cue bar UI show
     * completely.
     */
    public static final int CUJ_AMBIENT_CUE_SHOW = 145;

    /**
     * Tracks hide animation for Cue bar. Cue bar is a floating bar that appears on the screen to
     * provide personalized contextual actions based on the current app.
     *
     * <p>Tracking begins when the cue bar UI starts to hide (a user click the close button, switch
     * to another task, etc) and ends when cue bar UI disappears completely.
     */
    public static final int CUJ_AMBIENT_CUE_HIDE = 146;

    /**
     * Tracks expand animation for Cue bar. Cue bar is a floating bar that appears on the screen to
     * provide personalized contextual actions based on the current app.
     *
     * <p>Tracking begins when a user click the cue bar to expand the action list and ends then all
     * actions show.
     */
    public static final int CUJ_AMBIENT_CUE_EXPAND = 147;

    /**
     * Tracks collapse animation for Cue bar. Cue bar is a floating bar that appears on the screen
     * to provide personalized contextual actions based on the current app.
     *
     * <p>Tracking begins when the cue bar UI tap other region to collapse the action list and ends
     * when action list disappear completely.
     */
    public static final int CUJ_AMBIENT_CUE_COLLAPSE = 148;

    // When adding a CUJ, update this and make sure to also update CUJ_TO_STATSD_INTERACTION_TYPE.
    @VisibleForTesting static final int LAST_CUJ = CUJ_DESKTOP_MODE_MOVE_FROM_SPLIT_SCREEN;
    @VisibleForTesting static final int LAST_CUJ = CUJ_AMBIENT_CUE_COLLAPSE;

    /** @hide */
    @IntDef({
@@ -614,6 +650,10 @@ public class Cuj {
            CUJ_LAUNCHER_WIDGET_PICKER_APP_EXPAND,
            CUJ_DESKTOP_MODE_MOVE_TO_SPLIT_SCREEN,
            CUJ_DESKTOP_MODE_MOVE_FROM_SPLIT_SCREEN,
            CUJ_AMBIENT_CUE_SHOW,
            CUJ_AMBIENT_CUE_HIDE,
            CUJ_AMBIENT_CUE_EXPAND,
            CUJ_AMBIENT_CUE_COLLAPSE,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface CujType {}
@@ -1044,6 +1084,14 @@ public class Cuj {
                return "DESKTOP_MODE_MOVE_TO_SPLIT_SCREEN";
            case CUJ_DESKTOP_MODE_MOVE_FROM_SPLIT_SCREEN:
                return "DESKTOP_MODE_MOVE_FROM_SPLIT_SCREEN";
            case CUJ_AMBIENT_CUE_SHOW:
                return "AMBIENT_CUE_SHOW";
            case CUJ_AMBIENT_CUE_HIDE:
                return "AMBIENT_CUE_HIDE";
            case CUJ_AMBIENT_CUE_EXPAND:
                return "AMBIENT_CUE_EXPAND";
            case CUJ_AMBIENT_CUE_COLLAPSE:
                return "AMBIENT_CUE_COLLAPSE";
        }
        return "UNKNOWN";
    }
+8 −0
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.toSize
import com.android.compose.windowsizeclass.calculateWindowSizeClass
import com.android.systemui.ambientcue.ui.utils.AmbientCueAnimationState
import com.android.systemui.ambientcue.ui.viewmodel.ActionViewModel
import com.android.systemui.ambientcue.ui.viewmodel.AmbientCueViewModel
import com.android.systemui.ambientcue.ui.viewmodel.PillStyleViewModel
@@ -57,6 +58,7 @@ fun AmbientCueContainer(
    ambientCueViewModelFactory: AmbientCueViewModel.Factory,
    onShouldInterceptTouches: (Boolean, Rect?) -> Unit,
    modifier: Modifier = Modifier,
    onAnimationStateChange: (Int, AmbientCueAnimationState) -> Unit,
) {
    val viewModel = rememberViewModel("AmbientCueContainer") { ambientCueViewModelFactory.create() }

@@ -89,6 +91,7 @@ fun AmbientCueContainer(
                    expanded = expanded,
                    onShouldInterceptTouches = onShouldInterceptTouches,
                    modifier = Modifier.align(Alignment.BottomCenter),
                    onAnimationStateChange = onAnimationStateChange,
                )
            }
            is PillStyleViewModel.ShortPillStyle -> {
@@ -106,6 +109,7 @@ fun AmbientCueContainer(
                        } else {
                            Modifier
                        },
                    onAnimationStateChange = onAnimationStateChange,
                )
            }
            is PillStyleViewModel.Uninitialized -> {}
@@ -122,6 +126,7 @@ private fun TaskBarAnd3ButtonAmbientCue(
    pillPositionInWindow: Rect?,
    onShouldInterceptTouches: (Boolean, Rect?) -> Unit,
    modifier: Modifier = Modifier,
    onAnimationStateChange: (Int, AmbientCueAnimationState) -> Unit,
) {
    val configuration = LocalConfiguration.current
    val density = LocalDensity.current
@@ -216,6 +221,7 @@ private fun TaskBarAnd3ButtonAmbientCue(
            },
        onClick = { viewModel.expand() },
        onCloseClick = { viewModel.hide() },
        onAnimationStateChange = onAnimationStateChange,
    )
}

@@ -227,6 +233,7 @@ private fun NavBarAmbientCue(
    expanded: Boolean,
    onShouldInterceptTouches: (Boolean, Rect?) -> Unit,
    modifier: Modifier = Modifier,
    onAnimationStateChange: (Int, AmbientCueAnimationState) -> Unit,
) {
    val windowWidthSizeClass = calculateWindowSizeClass().widthSizeClass

@@ -271,6 +278,7 @@ private fun NavBarAmbientCue(
        },
        onCloseClick = { viewModel.hide() },
        onCloseEducation = { viewModel.disableFirstTimeHint() },
        onAnimationStateChange = onAnimationStateChange,
    )
}

+123 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.ambientcue.ui.compose

import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import com.android.internal.jank.Cuj
import com.android.systemui.ambientcue.ui.utils.AmbientCueAnimationState

@Composable
fun AmbientCueJankMonitorComposable(
    visibleTargetState: Boolean,
    enterProgress: Float,
    expanded: Boolean,
    expansionAlpha: Float,
    showAnimationInProgress: MutableState<Boolean>,
    hideAnimationInProgress: MutableState<Boolean>,
    expandAnimationInProgress: MutableState<Boolean>,
    collapseAnimationInProgress: MutableState<Boolean>,
    onAnimationStateChange: (Int, AmbientCueAnimationState) -> Unit,
) {
    LaunchedEffect(visibleTargetState, enterProgress) {
        if (visibleTargetState) {
            when (enterProgress) {
                0f -> {
                    showAnimationInProgress.value = true
                    onAnimationStateChange(Cuj.CUJ_AMBIENT_CUE_SHOW, AmbientCueAnimationState.BEGIN)
                }
                1f -> {
                    showAnimationInProgress.value = false
                    onAnimationStateChange(Cuj.CUJ_AMBIENT_CUE_SHOW, AmbientCueAnimationState.END)
                }
            }
        } else {
            when (enterProgress) {
                0f -> {
                    hideAnimationInProgress.value = false
                    onAnimationStateChange(Cuj.CUJ_AMBIENT_CUE_HIDE, AmbientCueAnimationState.END)
                }
                1f -> {
                    hideAnimationInProgress.value = true
                    onAnimationStateChange(Cuj.CUJ_AMBIENT_CUE_HIDE, AmbientCueAnimationState.BEGIN)
                }
            }
        }
    }
    LaunchedEffect(expanded, expansionAlpha) {
        if (expanded) {
            when (expansionAlpha) {
                0f -> {
                    if (expandAnimationInProgress.value) {
                        expandAnimationInProgress.value = false
                        onAnimationStateChange(
                            Cuj.CUJ_AMBIENT_CUE_EXPAND,
                            AmbientCueAnimationState.END,
                        )
                    }
                }
                1f -> {
                    expandAnimationInProgress.value = true
                    onAnimationStateChange(
                        Cuj.CUJ_AMBIENT_CUE_EXPAND,
                        AmbientCueAnimationState.BEGIN,
                    )
                }
            }
        } else {
            when (expansionAlpha) {
                0f -> {
                    collapseAnimationInProgress.value = true
                    onAnimationStateChange(
                        Cuj.CUJ_AMBIENT_CUE_COLLAPSE,
                        AmbientCueAnimationState.BEGIN,
                    )
                }
                1f -> {
                    if (collapseAnimationInProgress.value) {
                        collapseAnimationInProgress.value = false
                        onAnimationStateChange(
                            Cuj.CUJ_AMBIENT_CUE_COLLAPSE,
                            AmbientCueAnimationState.END,
                        )
                    }
                }
            }
        }
    }
    DisposableEffect(Unit) {
        onDispose {
            if (showAnimationInProgress.value) {
                onAnimationStateChange(Cuj.CUJ_AMBIENT_CUE_SHOW, AmbientCueAnimationState.CANCEL)
            }
            if (hideAnimationInProgress.value) {
                onAnimationStateChange(Cuj.CUJ_AMBIENT_CUE_HIDE, AmbientCueAnimationState.CANCEL)
            }
            if (expandAnimationInProgress.value) {
                onAnimationStateChange(Cuj.CUJ_AMBIENT_CUE_EXPAND, AmbientCueAnimationState.CANCEL)
            }
            if (collapseAnimationInProgress.value) {
                onAnimationStateChange(
                    Cuj.CUJ_AMBIENT_CUE_COLLAPSE,
                    AmbientCueAnimationState.CANCEL,
                )
            }
        }
    }
}
+17 −0
Original line number Diff line number Diff line
@@ -79,6 +79,7 @@ import androidx.compose.ui.util.fastForEachIndexed
import androidx.compose.ui.util.lerp
import com.android.compose.ui.graphics.painter.rememberDrawablePainter
import com.android.systemui.ambientcue.ui.compose.modifier.animatedActionBorder
import com.android.systemui.ambientcue.ui.utils.AmbientCueAnimationState
import com.android.systemui.ambientcue.ui.utils.FilterUtils
import com.android.systemui.ambientcue.ui.viewmodel.ActionType
import com.android.systemui.ambientcue.ui.viewmodel.ActionViewModel
@@ -95,6 +96,7 @@ fun NavBarPill(
    onClick: () -> Unit = {},
    onCloseClick: () -> Unit = {},
    onCloseEducation: () -> Unit = {},
    onAnimationStateChange: (Int, AmbientCueAnimationState) -> Unit = { _, _ -> },
) {
    val maxPillWidth = 248.dp
    val backgroundColor = if (isSystemInDarkTheme()) Color.Black else Color.White
@@ -103,6 +105,10 @@ fun NavBarPill(
    val density = LocalDensity.current
    val collapsedWidthPx = with(density) { navBarWidth.toPx() }
    var wasEverCollapsed by remember(actions) { mutableStateOf(false) }
    val showAnimationInProgress = remember { mutableStateOf(false) }
    val hideAnimationInProgress = remember { mutableStateOf(false) }
    val expandAnimationInProgress = remember { mutableStateOf(false) }
    val collapseAnimationInProgress = remember { mutableStateOf(false) }
    LaunchedEffect(expanded) {
        if (expanded) {
            wasEverCollapsed = true
@@ -156,6 +162,17 @@ fun NavBarPill(
            animationSpec = tween(250, delayMillis = 200),
            label = "smartScrimOffset",
        )
    AmbientCueJankMonitorComposable(
        visibleTargetState = visibleState.targetState,
        enterProgress = enterProgress,
        expanded = expanded,
        expansionAlpha = expansionAlpha,
        showAnimationInProgress = showAnimationInProgress,
        hideAnimationInProgress = hideAnimationInProgress,
        expandAnimationInProgress = expandAnimationInProgress,
        collapseAnimationInProgress = collapseAnimationInProgress,
        onAnimationStateChange = onAnimationStateChange,
    )

    val config = LocalConfiguration.current
    val isBoldTextEnabled = config.fontWeightAdjustment > 0
+18 −0
Original line number Diff line number Diff line
@@ -76,6 +76,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEach
import com.android.compose.ui.graphics.painter.rememberDrawablePainter
import com.android.systemui.ambientcue.ui.compose.modifier.animatedActionBorder
import com.android.systemui.ambientcue.ui.utils.AmbientCueAnimationState
import com.android.systemui.ambientcue.ui.utils.FilterUtils
import com.android.systemui.ambientcue.ui.viewmodel.ActionType
import com.android.systemui.ambientcue.ui.viewmodel.ActionViewModel
@@ -91,6 +92,7 @@ fun ShortPill(
    rotation: Int = 0,
    onClick: () -> Unit = {},
    onCloseClick: () -> Unit = {},
    onAnimationStateChange: (Int, AmbientCueAnimationState) -> Unit = { _, _ -> },
) {
    val outlineColor = if (isSystemInDarkTheme()) Color.White else Color.Black
    val backgroundColor = if (isSystemInDarkTheme()) Color.Black else Color.White
@@ -101,6 +103,10 @@ fun ShortPill(
    val shortPillBoxWidth = 48.dp
    val shortPillBoxLength = 68.dp
    val transitionTween: TweenSpec<Float> = tween(250, delayMillis = 200)
    val showAnimationInProgress = remember { mutableStateOf(false) }
    val hideAnimationInProgress = remember { mutableStateOf(false) }
    val expandAnimationInProgress = remember { mutableStateOf(false) }
    val collapseAnimationInProgress = remember { mutableStateOf(false) }

    val visibleState = remember { MutableTransitionState(false) }
    visibleState.targetState = visible
@@ -140,6 +146,18 @@ fun ShortPill(
            if (it) 0.4f else 0f
        }

    AmbientCueJankMonitorComposable(
        visibleTargetState = visibleState.targetState,
        enterProgress = enterProgress,
        expanded = expanded,
        expansionAlpha = expansionAlpha,
        showAnimationInProgress = showAnimationInProgress,
        hideAnimationInProgress = hideAnimationInProgress,
        expandAnimationInProgress = expandAnimationInProgress,
        collapseAnimationInProgress = collapseAnimationInProgress,
        onAnimationStateChange = onAnimationStateChange,
    )

    // State variables to store the measured size and position of the main pill.
    var pillContentSize by remember { mutableStateOf(IntSize.Zero) }
    var pillContentPosition by remember { mutableStateOf(Offset.Zero) }
Loading