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

Commit 005b6768 authored by Lucas Dupin's avatar Lucas Dupin
Browse files

Show short pill over the recents button

Flag: com.android.systemui.enable_underlay
Test: atest AmbientCueRepositoryTest
Test: atest AmbientCueInteractorTest
Test: atest AmbientCueViewModelTest
Bug: 415914274
Bug: 415914083
Change-Id: I8a6596d23eedffc92c45bcfb4c01abc2d9e8ba19
parent 476d1738
Loading
Loading
Loading
Loading
+23 −12
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.boundsInParent
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import com.android.systemui.ambientcue.ui.viewmodel.ActionViewModel
@@ -84,16 +85,16 @@ fun AmbientCueContainer(
                )
            }
            is PillStyleViewModel.ShortPillStyle -> {
                val pillCenterInWindow = pillStyle.position
                val pillPositionInWindow = pillStyle.position
                TaskBarAnd3ButtonAmbientCue(
                    viewModel = viewModel,
                    actions = actions,
                    visible = visible,
                    expanded = expanded,
                    pillCenterInWindow = pillCenterInWindow,
                    pillPositionInWindow = pillPositionInWindow,
                    modifier =
                        if (pillCenterInWindow == null) {
                            Modifier.align(Alignment.BottomCenter)
                        if (pillPositionInWindow == null) {
                            Modifier.align(Alignment.BottomEnd)
                        } else {
                            Modifier
                        },
@@ -110,17 +111,23 @@ private fun TaskBarAnd3ButtonAmbientCue(
    actions: List<ActionViewModel>,
    visible: Boolean,
    expanded: Boolean,
    pillCenterInWindow: Rect?,
    pillPositionInWindow: Rect?,
    modifier: Modifier = Modifier,
) {
    val configuration = LocalConfiguration.current
    val density = LocalDensity.current
    val portrait = configuration.orientation == Configuration.ORIENTATION_PORTRAIT
    var pillCenter by remember { mutableStateOf(Offset.Zero) }
    val screenHeightPx = with(density) { configuration.screenHeightDp.dp.toPx() }
    BackgroundGlow(
        visible = visible,
        expanded = expanded,
        collapsedOffset = IntOffset(0, 110),
        modifier = modifier.graphicsLayer { translationX = -size.width / 2 + pillCenter.x },
        modifier =
            modifier.graphicsLayer {
                translationX = -size.width / 2 + pillCenter.x
                translationY = screenHeightPx - size.height
            },
    )
    ShortPill(
        actions = actions,
@@ -128,16 +135,20 @@ private fun TaskBarAnd3ButtonAmbientCue(
        horizontal = portrait,
        expanded = expanded,
        modifier =
            if (pillCenterInWindow == null) {
            if (pillPositionInWindow == null) {
                modifier.padding(bottom = 12.dp, end = 24.dp).onGloballyPositioned {
                    pillCenter = it.boundsInParent().center
                }
            } else {
                Modifier.graphicsLayer {
                    val center = pillCenterInWindow.center
                    translationX = center.x - size.width / 2
                    translationY = center.y - size.height / 2
                    pillCenter = center
                        translationX = pillCenter.x - size.width / 2
                        translationY = pillCenter.y - size.height / 2
                    }
                    .onGloballyPositioned { layoutCoordinates ->
                        layoutCoordinates.parentCoordinates?.let { parentCoordinates ->
                            pillCenter =
                                parentCoordinates.screenToLocal(pillPositionInWindow.center)
                        }
                    }
            },
        onClick = { viewModel.expand() },
+6 −2
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package com.android.systemui.ambientcue.ui.viewmodel

import android.content.Context
import android.content.applicationContext
import android.graphics.Rect
import androidx.compose.ui.graphics.toComposeRect
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -154,12 +156,14 @@ class AmbientCueViewModelTest : SysuiTestCase() {
    @Test
    fun pillStyle_3ButtonNav_shortPill() =
        kosmos.runTest {
            val recentsButtonPosition = Rect(10, 20, 30, 40)
            ambientCueRepository.fake.setIsGestureNav(false)
            ambientCueRepository.fake.setTaskBarVisible(true)
            ambientCueRepository.fake.setRecentsButtonPosition(recentsButtonPosition)

            runCurrent()
            assertThat(viewModel.pillStyle)
                .isInstanceOf(PillStyleViewModel.ShortPillStyle::class.java)
            assertThat((viewModel.pillStyle as PillStyleViewModel.ShortPillStyle).position)
                .isEqualTo(recentsButtonPosition.toComposeRect())
        }

    @Test
+6 −1
Original line number Diff line number Diff line
@@ -178,5 +178,10 @@ interface ISystemUiProxy {
     */
    oneway void updateContextualEduStats(boolean isTrackpadGesture, String gestureType) = 58;

    // Next id = 59
    /**
     * Sent after layout is performed for the "recents" button and it is visible on screen.
     */
    oneway void notifyRecentsButtonPositionChanged(in Rect position) = 59;

    // Next id = 60
}
+14 −0
Original line number Diff line number Diff line
@@ -51,6 +51,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.ResolveInfo;
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.input.InputManager;
import android.hardware.input.InputManagerGlobal;
@@ -412,6 +413,12 @@ public class LauncherProxyService implements CallbackController<LauncherProxyLis
                    onTaskbarAutohideSuspend(suspend));
        }

        @Override
        public void notifyRecentsButtonPositionChanged(Rect position) {
            verifyCallerAndClearCallingIdentityPostMain("notifyRecentsButtonPositionChanged", () ->
                    onRecentsButtonPositionChanged(position));
        }

        private boolean sendEvent(int action, int code, int displayId) {
            long when = SystemClock.uptimeMillis();
            final KeyEvent ev = new KeyEvent(when, when, action, code, 0 /* repeat */,
@@ -1156,6 +1163,12 @@ public class LauncherProxyService implements CallbackController<LauncherProxyLis
        }
    }

    public void onRecentsButtonPositionChanged(Rect position) {
        for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
            mConnectionCallbacks.get(i).onRecentsButtonPositionChanged(position);
        }
    }

    private void notifyConnectionChanged() {
        for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
            mConnectionCallbacks.get(i).onConnectionChanged(mLauncherProxy != null);
@@ -1362,6 +1375,7 @@ public class LauncherProxyService implements CallbackController<LauncherProxyLis
        default void onTaskbarAutohideSuspend(boolean suspend) {}
        default void onAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {}
        default void onAssistantGestureCompletion(float velocity) {}
        default void onRecentsButtonPositionChanged(Rect position) {}
        default void startAssistant(Bundle bundle) {}
        default void setAssistantOverridesRequested(int[] invocationTypes) {}
        default void animateNavBarLongPress(boolean isTouchDown, boolean shrink, long durationMs) {}
+25 −18
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.app.smartspace.SmartspaceConfig
import android.app.smartspace.SmartspaceManager
import android.app.smartspace.SmartspaceSession.OnTargetsAvailableListener
import android.content.Context
import android.graphics.Rect
import android.util.Log
import android.view.autofill.AutofillId
import android.view.autofill.AutofillManager
@@ -50,6 +51,7 @@ import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -80,6 +82,8 @@ interface AmbientCueRepository {

    /** True if in gesture nav mode, false when in 3-button navbar. */
    val isGestureNav: StateFlow<Boolean>

    val recentsButtonPosition: StateFlow<Rect?>
}

@SysUISingleton
@@ -90,13 +94,27 @@ constructor(
    private val smartSpaceManager: SmartspaceManager?,
    private val autofillManager: AutofillManager?,
    private val activityStarter: ActivityStarter,
    private val launcherProxyService: LauncherProxyService,
    private val navigationModeController: NavigationModeController,
    @Background executor: Executor,
    @Application applicationContext: Context,
    focusdDisplayRepository: FocusedDisplayRepository,
    launcherProxyService: LauncherProxyService,
) : AmbientCueRepository {

    init {
        val callback =
            object : LauncherProxyListener {
                override fun onTaskbarStatusUpdated(visible: Boolean, stashed: Boolean) {
                    _isTaskBarVisible.update { visible && !stashed }
                }

                override fun onRecentsButtonPositionChanged(position: Rect?) {
                    _recentsButtonPosition.update { position }
                }
            }
        launcherProxyService.addCallback(callback)
    }

    override val actions: StateFlow<List<ActionModel>> =
        conflatedCallbackFlow {
                if (smartSpaceManager == null) {
@@ -201,23 +219,6 @@ constructor(
                initialValue = emptyList(),
            )

    override val isTaskBarVisible: StateFlow<Boolean> =
        conflatedCallbackFlow {
                val callback =
                    object : LauncherProxyListener {
                        override fun onTaskbarStatusUpdated(visible: Boolean, stashed: Boolean) {
                            trySend(visible && !stashed)
                        }
                    }
                launcherProxyService.addCallback(callback)
                awaitClose { launcherProxyService.removeCallback(callback) }
            }
            .stateIn(
                scope = backgroundScope,
                started = SharingStarted.WhileSubscribed(),
                initialValue = false,
            )

    override val isGestureNav: StateFlow<Boolean> =
        conflatedCallbackFlow {
                val listener =
@@ -230,6 +231,12 @@ constructor(
            }
            .stateIn(backgroundScope, SharingStarted.WhileSubscribed(), false)

    private val _isTaskBarVisible = MutableStateFlow(false)
    override val isTaskBarVisible: StateFlow<Boolean> = _isTaskBarVisible.asStateFlow()

    private val _recentsButtonPosition = MutableStateFlow<Rect?>(null)
    override val recentsButtonPosition: StateFlow<Rect?> = _recentsButtonPosition.asStateFlow()

    override val isImeVisible: MutableStateFlow<Boolean> = MutableStateFlow(false)

    override val isDeactivated: MutableStateFlow<Boolean> = MutableStateFlow(false)
Loading