Loading packages/SystemUI/compose/features/src/com/android/systemui/ambientcue/ui/compose/AmbientCueContainer.kt +23 −12 Original line number Diff line number Diff line Loading @@ -35,6 +35,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.compose.windowsizeclass.calculateWindowSizeClass Loading Loading @@ -83,16 +84,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 }, Loading @@ -109,17 +110,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, Loading @@ -127,16 +134,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() }, Loading packages/SystemUI/multivalentTests/src/com/android/systemui/ambientcue/ui/viewmodel/AmbientCueViewModelTest.kt +6 −2 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 Loading packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl +6 −1 Original line number Diff line number Diff line Loading @@ -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 } packages/SystemUI/src/com/android/systemui/LauncherProxyService.java +14 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 */, Loading Loading @@ -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); Loading Loading @@ -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) {} Loading packages/SystemUI/src/com/android/systemui/ambientcue/data/repository/AmbientCueRepository.kt +25 −18 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -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 Loading @@ -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) { Loading Loading @@ -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 = Loading @@ -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 Loading
packages/SystemUI/compose/features/src/com/android/systemui/ambientcue/ui/compose/AmbientCueContainer.kt +23 −12 Original line number Diff line number Diff line Loading @@ -35,6 +35,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.compose.windowsizeclass.calculateWindowSizeClass Loading Loading @@ -83,16 +84,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 }, Loading @@ -109,17 +110,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, Loading @@ -127,16 +134,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() }, Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/ambientcue/ui/viewmodel/AmbientCueViewModelTest.kt +6 −2 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 Loading
packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl +6 −1 Original line number Diff line number Diff line Loading @@ -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 }
packages/SystemUI/src/com/android/systemui/LauncherProxyService.java +14 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 */, Loading Loading @@ -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); Loading Loading @@ -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) {} Loading
packages/SystemUI/src/com/android/systemui/ambientcue/data/repository/AmbientCueRepository.kt +25 −18 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -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 Loading @@ -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) { Loading Loading @@ -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 = Loading @@ -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