diff options
46 files changed, 478 insertions, 314 deletions
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 4fe11e6ce274..5b39f62db261 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -6183,7 +6183,8 @@ public interface WindowManager extends ViewManager { } /** - * Returns the size of the provided insets. + * Returns the size of the provided insets. May be {@code null} if the provided insets have + * the same size as the window frame. */ public @Nullable Insets getInsetsSize() { return mInsets; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java index 8156a9c8d04a..f7f45ae36eda 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java @@ -235,7 +235,7 @@ public class DividerSnapAlgorithm { private SnapTarget snap(int position, boolean hardDismiss) { if (shouldApplyFreeSnapMode(position)) { - return new SnapTarget(position, position, SNAP_TO_NONE); + return new SnapTarget(position, SNAP_TO_NONE); } int minIndex = -1; float minDistance = Float.MAX_VALUE; @@ -263,7 +263,7 @@ public class DividerSnapAlgorithm { if (dockedSide == DOCKED_RIGHT) { startPos += mInsets.left; } - mTargets.add(new SnapTarget(startPos, startPos, SNAP_TO_START_AND_DISMISS, 0.35f)); + mTargets.add(new SnapTarget(startPos, SNAP_TO_START_AND_DISMISS, 0.35f)); switch (mSnapMode) { case SNAP_MODE_16_9: addRatio16_9Targets(isHorizontalDivision, dividerMax); @@ -278,7 +278,7 @@ public class DividerSnapAlgorithm { addMinimizedTarget(isHorizontalDivision, dockedSide); break; } - mTargets.add(new SnapTarget(dividerMax, dividerMax, SNAP_TO_END_AND_DISMISS, 0.35f)); + mTargets.add(new SnapTarget(dividerMax, SNAP_TO_END_AND_DISMISS, 0.35f)); } private void addNonDismissingTargets(boolean isHorizontalDivision, int topPosition, @@ -325,14 +325,14 @@ public class DividerSnapAlgorithm { */ private void maybeAddTarget(int position, int smallerSize, @SnapPosition int snapPosition) { if (smallerSize >= mMinimalSizeResizableTask) { - mTargets.add(new SnapTarget(position, position, snapPosition)); + mTargets.add(new SnapTarget(position, snapPosition)); } } private void addMiddleTarget(boolean isHorizontalDivision) { int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision, mInsets, mDisplayWidth, mDisplayHeight, mDividerSize); - mTargets.add(new SnapTarget(position, position, SNAP_TO_50_50)); + mTargets.add(new SnapTarget(position, SNAP_TO_50_50)); } private void addMinimizedTarget(boolean isHorizontalDivision, int dockedSide) { @@ -346,7 +346,7 @@ public class DividerSnapAlgorithm { position = mDisplayWidth - position - mInsets.right - mDividerSize; } } - mTargets.add(new SnapTarget(position, position, SNAP_TO_MINIMIZE)); + mTargets.add(new SnapTarget(position, SNAP_TO_MINIMIZE)); } public SnapTarget getMiddleTarget() { @@ -377,20 +377,15 @@ public class DividerSnapAlgorithm { } /** - * Represents a snap target for the divider. + * An object, calculated at boot time, representing a legal position for the split screen + * divider (i.e. the divider can be dragged to this spot). */ public static class SnapTarget { /** Position of this snap target. The right/bottom edge of the top/left task snaps here. */ public final int position; /** - * Like {@link #position}, but used to calculate the task bounds which might be different - * from the stack bounds. - */ - public final int taskPosition; - - /** - * An int describing the placement of the divider in this snap target. + * An int (enum) describing the placement of the divider in this snap target. */ public final @SnapPosition int snapPosition; @@ -402,14 +397,13 @@ public class DividerSnapAlgorithm { */ private final float distanceMultiplier; - public SnapTarget(int position, int taskPosition, @SnapPosition int snapPosition) { - this(position, taskPosition, snapPosition, 1f); + public SnapTarget(int position, @SnapPosition int snapPosition) { + this(position, snapPosition, 1f); } - public SnapTarget(int position, int taskPosition, @SnapPosition int snapPosition, + public SnapTarget(int position, @SnapPosition int snapPosition, float distanceMultiplier) { this.position = position; - this.taskPosition = taskPosition; this.snapPosition = snapPosition; this.distanceMultiplier = distanceMultiplier; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index 2a934cba1b50..b8aa1b189f7e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -72,11 +72,12 @@ import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.pip.PipUtils; +import com.android.wm.shell.common.split.DividerSnapAlgorithm.SnapTarget; +import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.shared.animation.Interpolators; import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition; import com.android.wm.shell.shared.split.SplitScreenConstants.SnapPosition; import com.android.wm.shell.shared.split.SplitScreenConstants.SplitPosition; -import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.splitscreen.StageTaskListener; import java.io.PrintWriter; @@ -543,7 +544,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange * to middle position if the provided SnapTarget is not supported. */ public void setDivideRatio(@PersistentSnapPosition int snapPosition) { - final DividerSnapAlgorithm.SnapTarget snapTarget = mDividerSnapAlgorithm.findSnapTarget( + final SnapTarget snapTarget = mDividerSnapAlgorithm.findSnapTarget( snapPosition); setDividerPosition(snapTarget != null @@ -577,7 +578,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange * Sets new divider position and updates bounds correspondingly. Notifies listener if the new * target indicates dismissing split. */ - public void snapToTarget(int currentPosition, DividerSnapAlgorithm.SnapTarget snapTarget) { + public void snapToTarget(int currentPosition, SnapTarget snapTarget) { switch (snapTarget.snapPosition) { case SNAP_TO_START_AND_DISMISS: flingDividerPosition(currentPosition, snapTarget.position, FLING_RESIZE_DURATION, @@ -613,10 +614,10 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange } /** - * Returns {@link DividerSnapAlgorithm.SnapTarget} which matches passing position and velocity. + * Returns {@link SnapTarget} which matches passing position and velocity. * If hardDismiss is set to {@code true}, it will be harder to reach dismiss target. */ - public DividerSnapAlgorithm.SnapTarget findSnapTarget(int position, float velocity, + public SnapTarget findSnapTarget(int position, float velocity, boolean hardDismiss) { return mDividerSnapAlgorithm.calculateSnapTarget(position, velocity, hardDismiss); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java index 09fcd8b02010..82b3a7de521b 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java @@ -20,8 +20,6 @@ import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_50_50; -import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_END_AND_DISMISS; -import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_START_AND_DISMISS; import static com.google.common.truth.Truth.assertThat; @@ -150,8 +148,8 @@ public class SplitLayoutTests extends ShellTestCase { @UiThreadTest public void testSnapToDismissStart() { // verify it callbacks properly when the snap target indicates dismissing split. - DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0 /* position */, - SNAP_TO_START_AND_DISMISS); + DividerSnapAlgorithm.SnapTarget snapTarget = + mSplitLayout.mDividerSnapAlgorithm.getDismissStartTarget(); mSplitLayout.snapToTarget(mSplitLayout.getDividerPosition(), snapTarget); waitDividerFlingFinished(); @@ -162,8 +160,8 @@ public class SplitLayoutTests extends ShellTestCase { @UiThreadTest public void testSnapToDismissEnd() { // verify it callbacks properly when the snap target indicates dismissing split. - DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0 /* position */, - SNAP_TO_END_AND_DISMISS); + DividerSnapAlgorithm.SnapTarget snapTarget = + mSplitLayout.mDividerSnapAlgorithm.getDismissEndTarget(); mSplitLayout.snapToTarget(mSplitLayout.getDividerPosition(), snapTarget); waitDividerFlingFinished(); @@ -203,9 +201,4 @@ public class SplitLayoutTests extends ShellTestCase { new Rect(0, 0, 1080, 2160)); return configuration; } - - private static DividerSnapAlgorithm.SnapTarget getSnapTarget(int position, int flag) { - return new DividerSnapAlgorithm.SnapTarget( - position /* position */, position /* taskPosition */, flag); - } } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt index b30f2b7002ce..0fc88b22a4d0 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt @@ -121,7 +121,7 @@ fun ContentScope.animateContentFloatAsState( @Deprecated( "Use animateContentFloatAsState() instead", - replaceWith = ReplaceWith("animateContentFloatAsState(value, key, canOverflow)") + replaceWith = ReplaceWith("animateContentFloatAsState(value, key, canOverflow)"), ) @Composable fun ContentScope.animateSceneFloatAsState( @@ -172,14 +172,11 @@ fun ContentScope.animateContentDpAsState( @Deprecated( "Use animateContentDpAsState() instead", - replaceWith = ReplaceWith("animateContentDpAsState(value, key, canOverflow)") + replaceWith = ReplaceWith("animateContentDpAsState(value, key, canOverflow)"), ) @Composable -fun ContentScope.animateSceneDpAsState( - value: Dp, - key: ValueKey, - canOverflow: Boolean = true, -) = animateContentDpAsState(value, key, canOverflow) +fun ContentScope.animateSceneDpAsState(value: Dp, key: ValueKey, canOverflow: Boolean = true) = + animateContentDpAsState(value, key, canOverflow) /** * Animate a shared element Dp value. @@ -214,10 +211,7 @@ private object SharedDpType : SharedValueType<Dp, Dp> { * @see ContentScope.animateContentValueAsState */ @Composable -fun ContentScope.animateContentColorAsState( - value: Color, - key: ValueKey, -): AnimatedState<Color> { +fun ContentScope.animateContentColorAsState(value: Color, key: ValueKey): AnimatedState<Color> { return animateContentValueAsState(value, key, SharedColorType, canOverflow = false) } @@ -227,10 +221,7 @@ fun ContentScope.animateContentColorAsState( * @see ElementScope.animateElementValueAsState */ @Composable -fun ElementScope<*>.animateElementColorAsState( - value: Color, - key: ValueKey, -): AnimatedState<Color> { +fun ElementScope<*>.animateElementColorAsState(value: Color, key: ValueKey): AnimatedState<Color> { return animateElementValueAsState(value, key, SharedColorType, canOverflow = false) } @@ -274,12 +265,7 @@ private object SharedColorType : SharedValueType<Color, ColorDelta> { * Note: This class is necessary because Color() checks the bounds of its values and UncheckedColor * is internal. */ -private class ColorDelta( - val red: Float, - val green: Float, - val blue: Float, - val alpha: Float, -) +private class ColorDelta(val red: Float, val green: Float, val blue: Float, val alpha: Float) @Composable internal fun <T> animateSharedValueAsState( @@ -331,7 +317,7 @@ internal fun <T> animateSharedValueAsState( private fun <T, Delta> sharedValue( layoutImpl: SceneTransitionLayoutImpl, key: ValueKey, - element: ElementKey? + element: ElementKey?, ): SharedValue<T, Delta> { return layoutImpl.sharedValues[key]?.get(element)?.let { it as SharedValue<T, Delta> } ?: error(valueReadTooEarlyMessage(key)) @@ -342,9 +328,7 @@ private fun valueReadTooEarlyMessage(key: ValueKey) = "means that you are reading it during composition, which you should not do. See the " + "documentation of AnimatedState for more information." -internal class SharedValue<T, Delta>( - val type: SharedValueType<T, Delta>, -) { +internal class SharedValue<T, Delta>(val type: SharedValueType<T, Delta>) { /** The target value of this shared value for each content. */ val targetValues = SnapshotStateMap<ContentKey, T>() diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt index 007b84a2954a..f38a31026664 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt @@ -109,12 +109,12 @@ internal class DraggableHandlerImpl( return (upOrLeft != null && contentTransition.isTransitioningBetween( fromContent.key, - upOrLeft.toContent(currentScene) + upOrLeft.toContent(currentScene), )) || (downOrRight != null && contentTransition.isTransitioningBetween( fromContent.key, - downOrRight.toContent(currentScene) + downOrRight.toContent(currentScene), )) } @@ -163,7 +163,7 @@ internal class DraggableHandlerImpl( private fun updateDragController( swipes: Swipes, - swipeAnimation: SwipeAnimation<*> + swipeAnimation: SwipeAnimation<*>, ): DragControllerImpl { val newDragController = DragControllerImpl(this, swipes, swipeAnimation) newDragController.updateTransition(swipeAnimation, force = true) @@ -171,10 +171,7 @@ internal class DraggableHandlerImpl( return newDragController } - internal fun createSwipeAnimation( - swipes: Swipes, - result: UserActionResult, - ): SwipeAnimation<*> { + internal fun createSwipeAnimation(swipes: Swipes, result: UserActionResult): SwipeAnimation<*> { val upOrLeftResult = swipes.upOrLeftResult val downOrRightResult = swipes.downOrRightResult val isUpOrLeft = @@ -266,7 +263,7 @@ private class DragControllerImpl( layoutState.startTransitionImmediately( animationScope = draggableHandler.layoutImpl.animationScope, newTransition.contentTransition, - true + true, ) } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt index a076f22a74d7..1061cc4f5989 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt @@ -263,7 +263,7 @@ internal class ElementNode( @ExperimentalComposeUiApi override fun MeasureScope.measure( measurable: Measurable, - constraints: Constraints + constraints: Constraints, ): MeasureResult { check(isLookingAhead) @@ -344,7 +344,7 @@ internal class ElementNode( private fun ApproachMeasureScope.doNotPlace( measurable: Measurable, - constraints: Constraints + constraints: Constraints, ): MeasureResult { recursivelyClearPlacementValues() stateInContent.lastSize = Element.SizeUnspecified @@ -355,7 +355,7 @@ internal class ElementNode( private fun ApproachMeasureScope.placeNormally( measurable: Measurable, - constraints: Constraints + constraints: Constraints, ): MeasureResult { val placeable = measurable.measure(constraints) stateInContent.lastSize = placeable.size() @@ -670,10 +670,7 @@ private fun prepareInterruption( * Reconcile the state of [element] in the formContent and toContent of [transition] so that the * values before interruption have their expected values, taking shared transitions into account. */ -private fun reconcileStates( - element: Element, - transition: TransitionState.Transition, -) { +private fun reconcileStates(element: Element, transition: TransitionState.Transition) { val fromContentState = element.stateByContent[transition.fromContent] ?: return val toContentState = element.stateByContent[transition.toContent] ?: return if (!isSharedElementEnabled(element.key, transition)) { @@ -1139,7 +1136,7 @@ private fun ContentDrawScope.getDrawScale( Offset.Unspecified } else { a.pivot.specifiedOrCenter() - b.pivot.specifiedOrCenter() - } + }, ) }, add = { a, b, bProgress -> @@ -1151,9 +1148,9 @@ private fun ContentDrawScope.getDrawScale( Offset.Unspecified } else { a.pivot.specifiedOrCenter() + b.pivot.specifiedOrCenter() * bProgress - } + }, ) - } + }, ) stateInContent.lastScale = interruptedScale @@ -1371,7 +1368,7 @@ private inline fun <T> computeValue( lerp( lerp(previewTargetValue, targetValueOrNull ?: idleValue, previewRangeProgress), idleValue, - transformation?.range?.progress(transition.progress) ?: transition.progress + transformation?.range?.progress(transition.progress) ?: transition.progress, ) } else { if (targetValueOrNull == null) { @@ -1384,7 +1381,7 @@ private inline fun <T> computeValue( lerp( lerp(idleValue, previewTargetValue, previewRangeProgress), targetValueOrNull, - transformation.range?.progress(transition.progress) ?: transition.progress + transformation.range?.progress(transition.progress) ?: transition.progress, ) } } @@ -1399,14 +1396,7 @@ private inline fun <T> computeValue( val idleValue = contentValue(contentState) val targetValue = - transformation.transform( - layoutImpl, - content, - element, - contentState, - transition, - idleValue, - ) + transformation.transform(layoutImpl, content, element, contentState, transition, idleValue) // Make sure we don't read progress if values are the same and we don't need to interpolate, so // we don't invalidate the phase where this is read. @@ -1433,7 +1423,7 @@ private inline fun <T> interpolateSharedElement( fromState: Element.State, toState: Element.State, isSpecified: (T) -> Boolean, - lerp: (T, T, Float) -> T + lerp: (T, T, Float) -> T, ): T { val start = contentValue(fromState) val end = contentValue(toState) diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/InterruptionHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/InterruptionHandler.kt index cb18c6729170..205714608e3c 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/InterruptionHandler.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/InterruptionHandler.kt @@ -79,9 +79,6 @@ object DefaultInterruptionHandler : InterruptionHandler { interrupted: TransitionState.Transition.ChangeScene, newTargetScene: SceneKey, ): InterruptionResult { - return InterruptionResult( - animateFrom = interrupted.currentScene, - chain = true, - ) + return InterruptionResult(animateFrom = interrupted.currentScene, chain = true) } } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt index ced177ccb9a0..f9a9eeb0d34f 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt @@ -49,10 +49,7 @@ sealed class ContentKey(debugName: String, identity: Any) : Key(debugName, ident } /** Key for a scene. */ -class SceneKey( - debugName: String, - identity: Any = Object(), -) : ContentKey(debugName, identity) { +class SceneKey(debugName: String, identity: Any = Object()) : ContentKey(debugName, identity) { override val testTag: String = "scene:$debugName" /** The unique [ElementKey] identifying this scene's root element. */ @@ -64,10 +61,7 @@ class SceneKey( } /** Key for an overlay. */ -class OverlayKey( - debugName: String, - identity: Any = Object(), -) : ContentKey(debugName, identity) { +class OverlayKey(debugName: String, identity: Any = Object()) : ContentKey(debugName, identity) { override val testTag: String = "overlay:$debugName" override fun toString(): String { diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt index 471ad3fee9fb..1f26b71cf3e5 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt @@ -86,7 +86,7 @@ private abstract class BaseElementScope<ContentScope>( value: T, key: ValueKey, type: SharedValueType<T, *>, - canOverflow: Boolean + canOverflow: Boolean, ): AnimatedState<T> { return animateSharedValueAsState( layoutImpl, @@ -200,7 +200,7 @@ private fun shouldComposeMovableElement( content, element, elementState, - isInContent = { contents.contains(it) } + isInContent = { contents.contains(it) }, ) } } @@ -220,11 +220,7 @@ private fun movableElementContentWhenIdle( elementState: TransitionState.Idle, ): ContentKey { val contents = element.contentPicker.contents - return elementContentWhenIdle( - layoutImpl, - elementState, - isInContent = { contents.contains(it) }, - ) + return elementContentWhenIdle(layoutImpl, elementState, isInContent = { contents.contains(it) }) } /** diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt index 8ae3a1145e24..9f2ac24b844e 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt @@ -57,10 +57,8 @@ sealed interface ObservableTransitionState { /** No transition/animation is currently running. */ data class Idle @JvmOverloads - constructor( - val currentScene: SceneKey, - val currentOverlays: Set<OverlayKey> = emptySet(), - ) : ObservableTransitionState + constructor(val currentScene: SceneKey, val currentOverlays: Set<OverlayKey> = emptySet()) : + ObservableTransitionState /** There is a transition animating between two scenes. */ sealed class Transition( @@ -219,10 +217,7 @@ fun SceneTransitionLayoutState.observableTransitionState(): Flow<ObservableTrans return snapshotFlow { when (val state = transitionState) { is TransitionState.Idle -> - ObservableTransitionState.Idle( - state.currentScene, - state.currentOverlays, - ) + ObservableTransitionState.Idle(state.currentScene, state.currentOverlays) is TransitionState.Transition.ChangeScene -> { ObservableTransitionState.Transition.ChangeScene( fromScene = state.fromScene, diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt index 8480d3afaed4..b00c8ade07eb 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt @@ -35,9 +35,7 @@ internal fun PredictiveBackHandler( layoutImpl: SceneTransitionLayoutImpl, result: UserActionResult?, ) { - PredictiveBackHandler( - enabled = result != null, - ) { events: Flow<BackEventCompat> -> + PredictiveBackHandler(enabled = result != null) { events: Flow<BackEventCompat> -> if (result == null) { // Note: We have to collect progress otherwise PredictiveBackHandler will throw. events.first() diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt index 004bb40bb0ad..f20548b1f81b 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt @@ -585,7 +585,7 @@ fun interface UserActionDistance { */ fun UserActionDistanceScope.absoluteDistance( fromSceneSize: IntSize, - orientation: Orientation + orientation: Orientation, ): Float } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt index f36c0fa2d75c..fe052344023c 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt @@ -253,7 +253,7 @@ internal class SceneTransitionLayoutImpl( key: OverlayKey, userActions: Map<UserAction, UserActionResult>, alignment: Alignment, - content: @Composable (ContentScope.() -> Unit) + content: @Composable (ContentScope.() -> Unit), ) { overlaysDefined = true overlaysToRemove.remove(key) @@ -291,7 +291,7 @@ internal class SceneTransitionLayoutImpl( private fun resolveUserActions( key: ContentKey, userActions: Map<UserAction, UserActionResult>, - layoutDirection: LayoutDirection + layoutDirection: LayoutDirection, ): Map<UserAction.Resolved, UserActionResult> { return userActions .mapKeys { it.key.resolve(layoutDirection) } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt index c2d5dd053148..dbff8a4f7f45 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt @@ -328,9 +328,7 @@ internal class MutableSceneTransitionLayoutStateImpl( ): Job { // Note that we start with UNDISPATCHED so that startTransition() is called directly and // transition becomes the current [transitionState] right after this call. - return animationScope.launch( - start = CoroutineStart.UNDISPATCHED, - ) { + return animationScope.launch(start = CoroutineStart.UNDISPATCHED) { startTransition(transition, chain) } } @@ -461,7 +459,7 @@ internal class MutableSceneTransitionLayoutStateImpl( val indicator = if (finishedTransitions.contains(transition)) "x" else " " appendLine(" [$indicator] $from => $to ($transition)") } - } + }, ) } @@ -621,7 +619,7 @@ internal class MutableSceneTransitionLayoutStateImpl( override fun showOverlay( overlay: OverlayKey, animationScope: CoroutineScope, - transitionKey: TransitionKey? + transitionKey: TransitionKey?, ) { checkThread() @@ -654,7 +652,7 @@ internal class MutableSceneTransitionLayoutStateImpl( ) { animate( replacedTransition = currentState, - reversed = overlay == currentState.fromContent + reversed = overlay == currentState.fromContent, ) } else { animate() @@ -664,7 +662,7 @@ internal class MutableSceneTransitionLayoutStateImpl( override fun hideOverlay( overlay: OverlayKey, animationScope: CoroutineScope, - transitionKey: TransitionKey? + transitionKey: TransitionKey?, ) { checkThread() @@ -705,7 +703,7 @@ internal class MutableSceneTransitionLayoutStateImpl( from: OverlayKey, to: OverlayKey, animationScope: CoroutineScope, - transitionKey: TransitionKey? + transitionKey: TransitionKey?, ) { checkThread() diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt index e65ed9b7dc97..b358faf2c418 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt @@ -50,7 +50,7 @@ internal constructor( private val transitionCache = mutableMapOf< ContentKey, - MutableMap<ContentKey, MutableMap<TransitionKey?, TransitionSpecImpl>> + MutableMap<ContentKey, MutableMap<TransitionKey?, TransitionSpecImpl>>, >() private val overscrollCache = @@ -70,7 +70,7 @@ internal constructor( private fun findSpec( from: ContentKey, to: ContentKey, - key: TransitionKey? + key: TransitionKey?, ): TransitionSpecImpl { val spec = transition(from, to, key) { it.from == from && it.to == to } if (spec != null) { @@ -250,7 +250,7 @@ internal class TransitionSpecImpl( override val to: ContentKey?, private val previewTransformationSpec: (() -> TransformationSpecImpl)? = null, private val reversePreviewTransformationSpec: (() -> TransformationSpecImpl)? = null, - private val transformationSpec: () -> TransformationSpecImpl + private val transformationSpec: () -> TransformationSpecImpl, ) : TransitionSpec { override fun reversed(): TransitionSpecImpl { return TransitionSpecImpl( @@ -265,9 +265,9 @@ internal class TransitionSpecImpl( progressSpec = reverse.progressSpec, swipeSpec = reverse.swipeSpec, distance = reverse.distance, - transformations = reverse.transformations.map { it.reversed() } + transformations = reverse.transformations.map { it.reversed() }, ) - } + }, ) } @@ -382,11 +382,7 @@ internal class TransformationSpecImpl( return ElementTransformations(shared, offset, size, drawScale, alpha) } - private fun throwIfNotNull( - previous: Transformation?, - element: ElementKey, - name: String, - ) { + private fun throwIfNotNull(previous: Transformation?, element: ElementKey, name: String) { if (previous != null) { error("$element has multiple $name transformations") } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt index 966bda410231..84dce0d730c4 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt @@ -54,7 +54,7 @@ internal fun createSwipeAnimation( result: UserActionResult, isUpOrLeft: Boolean, orientation: Orientation, - distance: Float = DistanceUnspecified + distance: Float = DistanceUnspecified, ): SwipeAnimation<*> { var lastDistance = distance diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt index dc7eda5b9cf6..98d4aaa91458 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt @@ -39,14 +39,14 @@ import com.android.compose.animation.scene.content.Content @Stable internal fun Modifier.swipeToScene( draggableHandler: DraggableHandlerImpl, - swipeDetector: SwipeDetector + swipeDetector: SwipeDetector, ): Modifier { return this.then(SwipeToSceneElement(draggableHandler, swipeDetector)) } private data class SwipeToSceneElement( val draggableHandler: DraggableHandlerImpl, - val swipeDetector: SwipeDetector + val swipeDetector: SwipeDetector, ) : ModifierNodeElement<SwipeToSceneNode>() { override fun create(): SwipeToSceneNode = SwipeToSceneNode(draggableHandler, swipeDetector) @@ -183,12 +183,12 @@ internal fun interface ScrollBehaviorOwner { */ private class ScrollBehaviorOwnerNode( override val traverseKey: Any, - val nestedScrollHandlerImpl: NestedScrollHandlerImpl + val nestedScrollHandlerImpl: NestedScrollHandlerImpl, ) : Modifier.Node(), TraversableNode, ScrollBehaviorOwner { override fun updateScrollBehaviors( topOrLeftBehavior: NestedScrollBehavior, bottomOrRightBehavior: NestedScrollBehavior, - isExternalOverscrollGesture: () -> Boolean + isExternalOverscrollGesture: () -> Boolean, ) { nestedScrollHandlerImpl.topOrLeftBehavior = topOrLeftBehavior nestedScrollHandlerImpl.bottomOrRightBehavior = bottomOrRightBehavior diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt index 1f82e0bd026a..763dc6bf49e0 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt @@ -333,7 +333,7 @@ object HighestZIndexContentPicker : ElementContentPicker { element: ElementKey, transition: TransitionState.Transition, fromContentZIndex: Float, - toContentZIndex: Float + toContentZIndex: Float, ): ContentKey { return if (fromContentZIndex > toContentZIndex) { transition.fromContent @@ -354,7 +354,7 @@ object HighestZIndexContentPicker : ElementContentPicker { element: ElementKey, transition: TransitionState.Transition, fromContentZIndex: Float, - toContentZIndex: Float + toContentZIndex: Float, ): ContentKey { return HighestZIndexContentPicker.contentDuringTransition( element, @@ -375,7 +375,7 @@ object LowestZIndexContentPicker : ElementContentPicker { element: ElementKey, transition: TransitionState.Transition, fromContentZIndex: Float, - toContentZIndex: Float + toContentZIndex: Float, ): ContentKey { return if (fromContentZIndex < toContentZIndex) { transition.fromContent @@ -396,7 +396,7 @@ object LowestZIndexContentPicker : ElementContentPicker { element: ElementKey, transition: TransitionState.Transition, fromContentZIndex: Float, - toContentZIndex: Float + toContentZIndex: Float, ): ContentKey { return LowestZIndexContentPicker.contentDuringTransition( element, @@ -423,9 +423,8 @@ object LowestZIndexContentPicker : ElementContentPicker { * is not the same as when going from scene B to scene A, so it's not usable in situations where * z-ordering during the transition matters. */ -class MovableElementContentPicker( - override val contents: Set<ContentKey>, -) : StaticElementContentPicker { +class MovableElementContentPicker(override val contents: Set<ContentKey>) : + StaticElementContentPicker { override fun contentDuringTransition( element: ElementKey, transition: TransitionState.Transition, @@ -501,7 +500,7 @@ interface PropertyTransformationBuilder { matcher: ElementMatcher, scaleX: Float = 1f, scaleY: Float = 1f, - pivot: Offset = Offset.Unspecified + pivot: Offset = Offset.Unspecified, ) /** diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt index da4c8d8db752..7ec5e4f4f149 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt @@ -41,9 +41,7 @@ import com.android.compose.animation.scene.transformation.Transformation import com.android.compose.animation.scene.transformation.TransformationRange import com.android.compose.animation.scene.transformation.Translate -internal fun transitionsImpl( - builder: SceneTransitionsBuilder.() -> Unit, -): SceneTransitions { +internal fun transitionsImpl(builder: SceneTransitionsBuilder.() -> Unit): SceneTransitions { val impl = SceneTransitionsBuilderImpl().apply(builder) return SceneTransitions( impl.defaultSwipeSpec, @@ -67,7 +65,7 @@ private class SceneTransitionsBuilderImpl : SceneTransitionsBuilder { key: TransitionKey?, preview: (TransitionBuilder.() -> Unit)?, reversePreview: (TransitionBuilder.() -> Unit)?, - builder: TransitionBuilder.() -> Unit + builder: TransitionBuilder.() -> Unit, ): TransitionSpec { return transition(from = null, to = to, key = key, preview, reversePreview, builder) } @@ -78,7 +76,7 @@ private class SceneTransitionsBuilderImpl : SceneTransitionsBuilder { key: TransitionKey?, preview: (TransitionBuilder.() -> Unit)?, reversePreview: (TransitionBuilder.() -> Unit)?, - builder: TransitionBuilder.() -> Unit + builder: TransitionBuilder.() -> Unit, ): TransitionSpec { return transition(from = from, to = to, key = key, preview, reversePreview, builder) } @@ -86,7 +84,7 @@ private class SceneTransitionsBuilderImpl : SceneTransitionsBuilder { override fun overscroll( content: ContentKey, orientation: Orientation, - builder: OverscrollBuilder.() -> Unit + builder: OverscrollBuilder.() -> Unit, ): OverscrollSpec { val impl = OverscrollBuilderImpl().apply(builder) check(impl.transformations.isNotEmpty()) { @@ -150,7 +148,7 @@ private class SceneTransitionsBuilderImpl : SceneTransitionsBuilder { to, previewTransformationSpec, reversePreviewTransformationSpec, - transformationSpec + transformationSpec, ) transitionSpecs.add(spec) return spec @@ -167,7 +165,7 @@ internal abstract class BaseTransitionBuilderImpl : BaseTransitionBuilder { start: Float?, end: Float?, easing: Easing, - builder: PropertyTransformationBuilder.() -> Unit + builder: PropertyTransformationBuilder.() -> Unit, ) { range = TransformationRange(start, end, easing) builder() @@ -202,7 +200,7 @@ internal abstract class BaseTransitionBuilderImpl : BaseTransitionBuilder { override fun translate( matcher: ElementMatcher, edge: Edge, - startsOutsideLayoutBounds: Boolean + startsOutsideLayoutBounds: Boolean, ) { transformation(EdgeTranslate(matcher, edge, startsOutsideLayoutBounds)) } @@ -256,7 +254,7 @@ internal class TransitionBuilderImpl : BaseTransitionBuilderImpl(), TransitionBu startMillis: Int?, endMillis: Int?, easing: Easing, - builder: PropertyTransformationBuilder.() -> Unit + builder: PropertyTransformationBuilder.() -> Unit, ) { if (startMillis != null && (startMillis < 0 || startMillis > durationMillis)) { error("invalid start value: startMillis=$startMillis durationMillis=$durationMillis") @@ -278,7 +276,7 @@ internal open class OverscrollBuilderImpl : BaseTransitionBuilderImpl(), Overscr override fun translate( matcher: ElementMatcher, x: OverscrollScope.() -> Float, - y: OverscrollScope.() -> Float + y: OverscrollScope.() -> Float, ) { transformation(OverscrollTranslate(matcher, x, y)) } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt index 9851b32c42c4..b7fa0d497200 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt @@ -19,9 +19,8 @@ package com.android.compose.animation.scene import androidx.compose.ui.geometry.Offset import androidx.compose.ui.unit.IntSize -internal class ElementStateScopeImpl( - private val layoutImpl: SceneTransitionLayoutImpl, -) : ElementStateScope { +internal class ElementStateScopeImpl(private val layoutImpl: SceneTransitionLayoutImpl) : + ElementStateScope { override fun ElementKey.targetSize(scene: SceneKey): IntSize? { return layoutImpl.elements[this]?.stateByContent?.get(scene)?.targetSize.takeIf { it != Element.SizeUnspecified @@ -39,9 +38,8 @@ internal class ElementStateScopeImpl( } } -internal class UserActionDistanceScopeImpl( - private val layoutImpl: SceneTransitionLayoutImpl, -) : UserActionDistanceScope, ElementStateScope by layoutImpl.elementStateScope { +internal class UserActionDistanceScopeImpl(private val layoutImpl: SceneTransitionLayoutImpl) : + UserActionDistanceScope, ElementStateScope by layoutImpl.elementStateScope { override val density: Float get() = layoutImpl.density.density diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt index 59dd896ad9ea..c8407b13db66 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt @@ -106,7 +106,7 @@ internal class ContentScopeImpl( override fun Element( key: ElementKey, modifier: Modifier, - content: @Composable (ElementScope<ElementContentScope>.() -> Unit) + content: @Composable (ElementScope<ElementContentScope>.() -> Unit), ) { Element(layoutImpl, this@ContentScopeImpl.content, key, modifier, content) } @@ -115,7 +115,7 @@ internal class ContentScopeImpl( override fun MovableElement( key: MovableElementKey, modifier: Modifier, - content: @Composable (ElementScope<MovableElementContentScope>.() -> Unit) + content: @Composable (ElementScope<MovableElementContentScope>.() -> Unit), ) { MovableElement(layoutImpl, this@ContentScopeImpl.content, key, modifier, content) } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt index a47caaa15f18..364c2036e02d 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt @@ -184,7 +184,7 @@ sealed interface TransitionState { private fun computeCurrentOverlays( include: OverlayKey, - exclude: OverlayKey + exclude: OverlayKey, ): Set<OverlayKey> { return buildSet { addAll(currentOverlaysWhenTransitionStarted) @@ -336,9 +336,7 @@ sealed interface TransitionState { return specForCurrentScene.transformationSpec.transformations.isNotEmpty() } - internal open fun interruptionProgress( - layoutImpl: SceneTransitionLayoutImpl, - ): Float { + internal open fun interruptionProgress(layoutImpl: SceneTransitionLayoutImpl): Float { if (!layoutImpl.state.enableInterruptions) { return 0f } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/modifiers/SizeMatcher.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/modifiers/SizeMatcher.kt index a4bd2be45297..4698e5849d02 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/modifiers/SizeMatcher.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/modifiers/SizeMatcher.kt @@ -119,9 +119,8 @@ fun Modifier.sizeMatcherDestination(matcher: SizeMatcher): Modifier { return this.then(SizeMatcherDestinationElement(matcher)) } -private data class SizeMatcherSourceNodeElement( - private val matcher: SizeMatcher, -) : ModifierNodeElement<SizeMatcherSourceNode>() { +private data class SizeMatcherSourceNodeElement(private val matcher: SizeMatcher) : + ModifierNodeElement<SizeMatcherSourceNode>() { override fun create(): SizeMatcherSourceNode = SizeMatcherSourceNode(matcher) override fun update(node: SizeMatcherSourceNode) { @@ -129,9 +128,8 @@ private data class SizeMatcherSourceNodeElement( } } -private class SizeMatcherSourceNode( - private var matcher: SizeMatcher, -) : Modifier.Node(), LayoutModifierNode { +private class SizeMatcherSourceNode(private var matcher: SizeMatcher) : + Modifier.Node(), LayoutModifierNode { override fun onAttach() { matcher.source = this } @@ -150,7 +148,7 @@ private class SizeMatcherSourceNode( override fun MeasureScope.measure( measurable: Measurable, - constraints: Constraints + constraints: Constraints, ): MeasureResult { return measurable.measure(constraints).run { matcher.sourceSize = IntSize(width, height) @@ -159,9 +157,8 @@ private class SizeMatcherSourceNode( } } -private data class SizeMatcherDestinationElement( - private val matcher: SizeMatcher, -) : ModifierNodeElement<SizeMatcherDestinationNode>() { +private data class SizeMatcherDestinationElement(private val matcher: SizeMatcher) : + ModifierNodeElement<SizeMatcherDestinationNode>() { override fun create(): SizeMatcherDestinationNode = SizeMatcherDestinationNode(matcher) override fun update(node: SizeMatcherDestinationNode) { @@ -169,9 +166,8 @@ private data class SizeMatcherDestinationElement( } } -private class SizeMatcherDestinationNode( - private var matcher: SizeMatcher, -) : Modifier.Node(), LayoutModifierNode { +private class SizeMatcherDestinationNode(private var matcher: SizeMatcher) : + Modifier.Node(), LayoutModifierNode { override fun onAttach() { this.matcher.destinations.add(this) } @@ -190,7 +186,7 @@ private class SizeMatcherDestinationNode( override fun MeasureScope.measure( measurable: Measurable, - constraints: Constraints + constraints: Constraints, ): MeasureResult { val preferredSize = matcher.sourceSize val preferredConstraints = Constraints.fixed(preferredSize.width, preferredSize.height) diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt index 05878c2d3f08..86e06ab1f243 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt @@ -61,15 +61,9 @@ internal class AnchoredTranslate( val offset = anchorToOffset - anchorFromOffset return if (content == transition.toContent) { - Offset( - value.x - offset.x, - value.y - offset.y, - ) + Offset(value.x - offset.x, value.y - offset.y) } else { - Offset( - value.x + offset.x, - value.y + offset.y, - ) + Offset(value.x + offset.x, value.y + offset.y) } } } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt index a32c7dd09f95..031f50e17225 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt @@ -36,7 +36,7 @@ internal class EdgeTranslate( element: Element, stateInContent: Element.State, transition: TransitionState.Transition, - value: Offset + value: Offset, ): Offset { val sceneSize = layoutImpl.content(content).targetSize val elementSize = stateInContent.targetSize diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt index 4528eefedb3e..078aa0f7efe9 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt @@ -23,16 +23,14 @@ import com.android.compose.animation.scene.SceneTransitionLayoutImpl import com.android.compose.animation.scene.content.state.TransitionState /** Fade an element in or out. */ -internal class Fade( - override val matcher: ElementMatcher, -) : PropertyTransformation<Float> { +internal class Fade(override val matcher: ElementMatcher) : PropertyTransformation<Float> { override fun transform( layoutImpl: SceneTransitionLayoutImpl, content: ContentKey, element: Element, stateInContent: Element.State, transition: TransitionState.Transition, - value: Float + value: Float, ): Float { // Return the alpha value of [element] either when it starts fading in or when it finished // fading out, which is `0` in both cases. diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt index 505ad04c598c..9bb302307359 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt @@ -83,17 +83,13 @@ internal class RangedPropertyTransformation<T>( override fun reversed(): Transformation { return RangedPropertyTransformation( delegate.reversed() as PropertyTransformation<T>, - range.reversed() + range.reversed(), ) } } /** The progress-based range of a [PropertyTransformation]. */ -data class TransformationRange( - val start: Float, - val end: Float, - val easing: Easing, -) { +data class TransformationRange(val start: Float, val end: Float, val easing: Easing) { constructor( start: Float? = null, end: Float? = null, diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt index 8f845866a0f3..70142717fffe 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt @@ -40,12 +40,7 @@ internal class Translate( transition: TransitionState.Transition, value: Offset, ): Offset { - return with(layoutImpl.density) { - Offset( - value.x + x.toPx(), - value.y + y.toPx(), - ) - } + return with(layoutImpl.density) { Offset(value.x + x.toPx(), value.y + y.toPx()) } } } @@ -71,10 +66,7 @@ internal class OverscrollTranslate( val overscrollScope = cachedOverscrollScope.getFromCacheOrCompute(layoutImpl.density, overscrollProperties) - return Offset( - x = value.x + overscrollScope.x(), - y = value.y + overscrollScope.y(), - ) + return Offset(x = value.x + overscrollScope.x(), y = value.y + overscrollScope.y()) } } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/StateLink.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/StateLink.kt index c830ca4fa7c0..2aec5091c3e5 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/StateLink.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/StateLink.kt @@ -50,9 +50,7 @@ class StateLink(target: SceneTransitionLayoutState, val transitionLinks: List<Tr error("From and To can't be the same") } - internal fun isMatchingLink( - transition: TransitionState.Transition, - ): Boolean { + internal fun isMatchingLink(transition: TransitionState.Transition): Boolean { return (sourceFrom == null || sourceFrom == transition.fromContent) && (sourceTo == null || sourceTo == transition.toContent) } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/grid/Grids.kt b/packages/SystemUI/compose/scene/src/com/android/compose/grid/Grids.kt index 790665aebe3e..f49939ba3a2d 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/grid/Grids.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/grid/Grids.kt @@ -99,10 +99,7 @@ private fun Grid( } } - Layout( - modifier = modifier, - content = content, - ) { measurables, constraints -> + Layout(modifier = modifier, content = content) { measurables, constraints -> val cells = measurables.size val columns: Int val rows: Int @@ -142,7 +139,7 @@ private fun Grid( (constraints.maxHeight - totalVerticalSpacingBetweenChildren) / rows } else { Constraints.Infinity - } + }, ) val placeables = buildList { diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/MathHelpers.kt b/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/MathHelpers.kt index e78ab297bfba..0447c36ea3f4 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/MathHelpers.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/MathHelpers.kt @@ -27,7 +27,7 @@ import com.android.compose.animation.scene.Scale fun lerp(start: IntSize, stop: IntSize, fraction: Float): IntSize { return IntSize( lerp(start.width, stop.width, fraction), - lerp(start.height, stop.height, fraction) + lerp(start.height, stop.height, fraction), ) } @@ -43,6 +43,6 @@ fun lerp(start: Scale, stop: Scale, fraction: Float): Scale { return Scale( lerp(start.scaleX, stop.scaleX, fraction), lerp(start.scaleY, stop.scaleY, fraction), - pivot + pivot, ) } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/SpaceVectorConverter.kt b/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/SpaceVectorConverter.kt index a13e9441523a..f08a18046537 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/SpaceVectorConverter.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/SpaceVectorConverter.kt @@ -22,8 +22,11 @@ import androidx.compose.ui.unit.Velocity interface SpaceVectorConverter { fun Offset.toFloat(): Float + fun Velocity.toFloat(): Float + fun Float.toOffset(): Offset + fun Float.toVelocity(): Velocity } @@ -36,15 +39,21 @@ fun SpaceVectorConverter(orientation: Orientation) = private val HorizontalConverter = object : SpaceVectorConverter { override fun Offset.toFloat() = x + override fun Velocity.toFloat() = x + override fun Float.toOffset() = Offset(this, 0f) + override fun Float.toVelocity() = Velocity(this, 0f) } private val VerticalConverter = object : SpaceVectorConverter { override fun Offset.toFloat() = y + override fun Velocity.toFloat() = y + override fun Float.toOffset() = Offset(0f, this) + override fun Float.toVelocity() = Velocity(0f, this) } diff --git a/packages/SystemUI/compose/scene/src/com/android/systemui/communal/ui/compose/CommunalSwipeDetector.kt b/packages/SystemUI/compose/scene/src/com/android/systemui/communal/ui/compose/CommunalSwipeDetector.kt index 41b015a2ede8..00e5405dd904 100644 --- a/packages/SystemUI/compose/scene/src/com/android/systemui/communal/ui/compose/CommunalSwipeDetector.kt +++ b/packages/SystemUI/compose/scene/src/com/android/systemui/communal/ui/compose/CommunalSwipeDetector.kt @@ -43,7 +43,7 @@ class CommunalSwipeDetector(private var lastDirection: SwipeSource.Resolved? = n layoutSize: IntSize, position: IntOffset, density: Density, - orientation: Orientation + orientation: Orientation, ): SwipeSource.Resolved? { return lastDirection } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadStatsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadStatsInteractorTest.kt index c4fc13227eef..98e09474d5f2 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadStatsInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadStatsInteractorTest.kt @@ -24,12 +24,17 @@ import com.android.systemui.contextualeducation.GestureType.BACK import com.android.systemui.coroutines.collectLastValue import com.android.systemui.education.data.repository.contextualEducationRepository import com.android.systemui.education.data.repository.fakeEduClock +import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType +import com.android.systemui.inputdevice.tutorial.tutorialSchedulerRepository import com.android.systemui.keyboard.data.repository.keyboardRepository import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.android.systemui.touchpad.data.repository.touchpadRepository import com.google.common.truth.Truth.assertThat +import kotlin.time.Duration.Companion.seconds +import kotlinx.coroutines.launch import kotlinx.coroutines.test.runTest +import org.junit.After import org.junit.Test import org.junit.runner.RunWith @@ -42,10 +47,15 @@ class KeyboardTouchpadStatsInteractorTest : SysuiTestCase() { private val keyboardRepository = kosmos.keyboardRepository private val touchpadRepository = kosmos.touchpadRepository private val repository = kosmos.contextualEducationRepository + private val fakeClock = kosmos.fakeEduClock + private val tutorialSchedulerRepository = kosmos.tutorialSchedulerRepository + private val initialDelayElapsedDuration = + KeyboardTouchpadEduStatsInteractorImpl.initialDelayDuration + 1.seconds @Test fun dataUpdatedOnIncrementSignalCountWhenTouchpadConnected() = testScope.runTest { + setUpForInitialDelayElapse() touchpadRepository.setIsAnyTouchpadConnected(true) val model by collectLastValue(repository.readGestureEduModelFlow(BACK)) @@ -58,6 +68,7 @@ class KeyboardTouchpadStatsInteractorTest : SysuiTestCase() { @Test fun dataUnchangedOnIncrementSignalCountWhenTouchpadDisconnected() = testScope.runTest { + setUpForInitialDelayElapse() touchpadRepository.setIsAnyTouchpadConnected(false) val model by collectLastValue(repository.readGestureEduModelFlow(BACK)) @@ -70,6 +81,7 @@ class KeyboardTouchpadStatsInteractorTest : SysuiTestCase() { @Test fun dataUpdatedOnIncrementSignalCountWhenKeyboardConnected() = testScope.runTest { + setUpForInitialDelayElapse() keyboardRepository.setIsAnyKeyboardConnected(true) val model by collectLastValue(repository.readGestureEduModelFlow(ALL_APPS)) @@ -82,6 +94,7 @@ class KeyboardTouchpadStatsInteractorTest : SysuiTestCase() { @Test fun dataUnchangedOnIncrementSignalCountWhenKeyboardDisconnected() = testScope.runTest { + setUpForInitialDelayElapse() keyboardRepository.setIsAnyKeyboardConnected(false) val model by collectLastValue(repository.readGestureEduModelFlow(ALL_APPS)) @@ -99,4 +112,61 @@ class KeyboardTouchpadStatsInteractorTest : SysuiTestCase() { underTest.updateShortcutTriggerTime(BACK) assertThat(model?.lastShortcutTriggeredTime).isEqualTo(kosmos.fakeEduClock.instant()) } + + @Test + fun dataUpdatedOnIncrementSignalCountAfterInitialDelay() = + testScope.runTest { + setUpForDeviceConnection() + tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, fakeClock.instant()) + + fakeClock.offset(initialDelayElapsedDuration) + val model by collectLastValue(repository.readGestureEduModelFlow(BACK)) + val originalValue = model!!.signalCount + underTest.incrementSignalCount(BACK) + + assertThat(model?.signalCount).isEqualTo(originalValue + 1) + } + + @Test + fun dataUnchangedOnIncrementSignalCountBeforeInitialDelay() = + testScope.runTest { + setUpForDeviceConnection() + tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, fakeClock.instant()) + + // No offset to the clock to simulate update before initial delay + val model by collectLastValue(repository.readGestureEduModelFlow(BACK)) + val originalValue = model!!.signalCount + underTest.incrementSignalCount(BACK) + + assertThat(model?.signalCount).isEqualTo(originalValue) + } + + @Test + fun dataUnchangedOnIncrementSignalCountWithoutOobeLaunchTime() = + testScope.runTest { + // No update to OOBE launch time to simulate no OOBE is launched yet + setUpForDeviceConnection() + + val model by collectLastValue(repository.readGestureEduModelFlow(BACK)) + val originalValue = model!!.signalCount + underTest.incrementSignalCount(BACK) + + assertThat(model?.signalCount).isEqualTo(originalValue) + } + + private suspend fun setUpForInitialDelayElapse() { + tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, fakeClock.instant()) + tutorialSchedulerRepository.updateLaunchTime(DeviceType.KEYBOARD, fakeClock.instant()) + fakeClock.offset(initialDelayElapsedDuration) + } + + private fun setUpForDeviceConnection() { + touchpadRepository.setIsAnyTouchpadConnected(true) + keyboardRepository.setIsAnyKeyboardConnected(true) + } + + @After + fun clear() { + testScope.launch { tutorialSchedulerRepository.clearDataStore() } + } } diff --git a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduStatsInteractor.kt b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduStatsInteractor.kt index eb4eee204834..0e2d9b6a3ae0 100644 --- a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduStatsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduStatsInteractor.kt @@ -16,15 +16,23 @@ package com.android.systemui.education.domain.interactor +import android.os.SystemProperties import com.android.systemui.contextualeducation.GestureType import com.android.systemui.contextualeducation.GestureType.ALL_APPS import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.education.dagger.ContextualEducationModule.EduClock import com.android.systemui.inputdevice.data.repository.UserInputDeviceRepository import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.KEYBOARD import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.TOUCHPAD +import com.android.systemui.inputdevice.tutorial.data.repository.TutorialSchedulerRepository +import java.time.Clock import javax.inject.Inject +import kotlin.time.Duration +import kotlin.time.Duration.Companion.hours +import kotlin.time.DurationUnit +import kotlin.time.toDuration import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch @@ -47,12 +55,24 @@ constructor( @Background private val backgroundScope: CoroutineScope, private val contextualEducationInteractor: ContextualEducationInteractor, private val inputDeviceRepository: UserInputDeviceRepository, + private val tutorialRepository: TutorialSchedulerRepository, + @EduClock private val clock: Clock, ) : KeyboardTouchpadEduStatsInteractor { + companion object { + val initialDelayDuration: Duration + get() = + SystemProperties.getLong( + "persist.contextual_edu.initial_delay_sec", + /* defaultValue= */ 72.hours.inWholeSeconds + ) + .toDuration(DurationUnit.SECONDS) + } + override fun incrementSignalCount(gestureType: GestureType) { backgroundScope.launch { val targetDevice = getTargetDevice(gestureType) - if (isTargetDeviceConnected(targetDevice)) { + if (isTargetDeviceConnected(targetDevice) && hasInitialDelayElapsed(targetDevice)) { contextualEducationInteractor.incrementSignalCount(gestureType) } } @@ -65,12 +85,10 @@ constructor( } private suspend fun isTargetDeviceConnected(deviceType: DeviceType): Boolean { - if (deviceType == KEYBOARD) { - return inputDeviceRepository.isAnyKeyboardConnectedForUser.first().isConnected - } else if (deviceType == TOUCHPAD) { - return inputDeviceRepository.isAnyTouchpadConnectedForUser.first().isConnected + return when (deviceType) { + KEYBOARD -> inputDeviceRepository.isAnyKeyboardConnectedForUser.first().isConnected + TOUCHPAD -> inputDeviceRepository.isAnyTouchpadConnectedForUser.first().isConnected } - return false } /** @@ -83,4 +101,11 @@ constructor( ALL_APPS -> KEYBOARD else -> TOUCHPAD } + + private suspend fun hasInitialDelayElapsed(deviceType: DeviceType): Boolean { + val oobeLaunchTime = tutorialRepository.launchTime(deviceType) ?: return false + return clock + .instant() + .isAfter(oobeLaunchTime.plusSeconds(initialDelayDuration.inWholeSeconds)) + } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java index 9a38358a2768..4b1504f1cc2f 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java @@ -28,9 +28,9 @@ import android.app.TaskInfo; import android.app.WindowConfiguration; import android.app.assist.AssistContent; import android.content.ClipData; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.graphics.Bitmap; @@ -249,7 +249,6 @@ final class AppClipsViewModel extends ViewModel { .map(taskInfo -> new InternalTaskInfo(taskInfo.topActivityInfo, taskInfo.taskId, taskInfo.userId, getPackageManagerForUser(taskInfo.userId))) - .filter(this::canAppStartThroughLauncher) .map(this::getBacklinksDataForTaskInfo) .toList(), mBgExecutor); @@ -257,6 +256,17 @@ final class AppClipsViewModel extends ViewModel { return Futures.transformAsync(backlinksNestedListFuture, Futures::allAsList, mBgExecutor); } + private PackageManager getPackageManagerForUser(int userId) { + // If app clips was launched as the same user, then reuse the available PM from mContext. + if (mContext.getUserId() == userId) { + return mContext.getPackageManager(); + } + + // PackageManager required for a different user, create its context and return its PM. + UserHandle userHandle = UserHandle.of(userId); + return mContext.createContextAsUser(userHandle, /* flags= */ 0).getPackageManager(); + } + /** * Returns all tasks on a given display after querying {@link IActivityTaskManager} from the * {@link #mBgExecutor}. @@ -311,17 +321,6 @@ final class AppClipsViewModel extends ViewModel { } /** - * Returns whether the app represented by the {@link InternalTaskInfo} can be launched through - * the all apps tray by a user. - */ - private boolean canAppStartThroughLauncher(InternalTaskInfo internalTaskInfo) { - // Use Intent.resolveActivity API to check if the intent resolves as that is what Android - // uses internally when apps use Context.startActivity. - return getMainLauncherIntentForTask(internalTaskInfo) - .resolveActivity(internalTaskInfo.getPackageManager()) != null; - } - - /** * Returns an {@link InternalBacklinksData} that represents the Backlink data internally, which * is captured by querying the system using {@link TaskInfo#taskId}. */ @@ -390,11 +389,14 @@ final class AppClipsViewModel extends ViewModel { internalTaskInfo.getTaskId(), internalTaskInfo.getTopActivityNameForDebugLogging())); - String appName = internalTaskInfo.getTopActivityAppName(); - Drawable appIcon = internalTaskInfo.getTopActivityAppIcon(); - ClipData mainLauncherIntent = ClipData.newIntent(appName, - getMainLauncherIntentForTask(internalTaskInfo)); - InternalBacklinksData fallback = new BacklinksData(mainLauncherIntent, appIcon); + String screenshottedAppName = internalTaskInfo.getTopActivityAppName(); + Drawable screenshottedAppIcon = internalTaskInfo.getTopActivityAppIcon(); + Intent screenshottedAppMainLauncherIntent = getMainLauncherIntentForTask( + internalTaskInfo.getTopActivityPackageName(), internalTaskInfo.getPackageManager()); + ClipData screenshottedAppMainLauncherClipData = + ClipData.newIntent(screenshottedAppName, screenshottedAppMainLauncherIntent); + InternalBacklinksData fallback = + new BacklinksData(screenshottedAppMainLauncherClipData, screenshottedAppIcon); if (content == null) { return fallback; } @@ -406,10 +408,14 @@ final class AppClipsViewModel extends ViewModel { Uri uri = content.getWebUri(); Intent backlinksIntent = new Intent(ACTION_VIEW).setData(uri); - if (doesIntentResolveToSameTask(backlinksIntent, internalTaskInfo)) { + BacklinkDisplayInfo backlinkDisplayInfo = getInfoThatResolvesIntent(backlinksIntent, + internalTaskInfo); + if (backlinkDisplayInfo != null) { DebugLogger.INSTANCE.logcatMessage(this, () -> "getBacklinksDataFromAssistContent: using app provided uri"); - return new BacklinksData(ClipData.newRawUri(appName, uri), appIcon); + return new BacklinksData( + ClipData.newRawUri(backlinkDisplayInfo.getDisplayLabel(), uri), + backlinkDisplayInfo.getAppIcon()); } } @@ -419,10 +425,14 @@ final class AppClipsViewModel extends ViewModel { () -> "getBacklinksDataFromAssistContent: app has provided an intent"); Intent backlinksIntent = content.getIntent(); - if (doesIntentResolveToSameTask(backlinksIntent, internalTaskInfo)) { + BacklinkDisplayInfo backlinkDisplayInfo = getInfoThatResolvesIntent(backlinksIntent, + internalTaskInfo); + if (backlinkDisplayInfo != null) { DebugLogger.INSTANCE.logcatMessage(this, () -> "getBacklinksDataFromAssistContent: using app provided intent"); - return new BacklinksData(ClipData.newIntent(appName, backlinksIntent), appIcon); + return new BacklinksData( + ClipData.newIntent(backlinkDisplayInfo.getDisplayLabel(), backlinksIntent), + backlinkDisplayInfo.getAppIcon()); } } @@ -431,27 +441,76 @@ final class AppClipsViewModel extends ViewModel { return fallback; } - private boolean doesIntentResolveToSameTask(Intent intentToResolve, - InternalTaskInfo requiredTaskInfo) { - PackageManager packageManager = requiredTaskInfo.getPackageManager(); - ComponentName resolvedComponent = intentToResolve.resolveActivity(packageManager); - if (resolvedComponent == null) { - return false; + /** + * Returns {@link BacklinkDisplayInfo} for the app that would resolve the provided backlink + * {@link Intent}. + * + * <p>The method uses the {@link PackageManager} available in the provided + * {@link InternalTaskInfo}. + * + * <p>This method returns {@code null} if Android is not able to resolve the backlink intent or + * if the resolved app does not have an icon in the launcher. + */ + @Nullable + private BacklinkDisplayInfo getInfoThatResolvesIntent(Intent backlinkIntent, + InternalTaskInfo internalTaskInfo) { + PackageManager packageManager = internalTaskInfo.getPackageManager(); + + // Query for all available activities as there is a chance that multiple apps could resolve + // the intent. In such cases the normal `intent.resolveActivity` API returns the activity + // resolver info which isn't helpful for further checks. Also, using MATCH_DEFAULT_ONLY flag + // is required as that flag will be used when the notes app builds the intent and calls + // startActivity with the intent. + List<ResolveInfo> resolveInfos = packageManager.queryIntentActivities(backlinkIntent, + PackageManager.MATCH_DEFAULT_ONLY); + if (resolveInfos.isEmpty()) { + DebugLogger.INSTANCE.logcatMessage(this, + () -> "getInfoThatResolvesIntent: could not resolve backlink intent"); + return null; + } + + // Only use the first result as the list is ordered from best match to worst and Android + // will also use the best match with `intent.startActivity` API which notes app will use. + ActivityInfo activityInfo = resolveInfos.get(0).activityInfo; + if (activityInfo == null) { + DebugLogger.INSTANCE.logcatMessage(this, + () -> "getInfoThatResolvesIntent: could not find activity info for backlink " + + "intent"); + return null; + } + + // Ignore resolved backlink app if users cannot start it through all apps tray. + if (!canAppStartThroughLauncher(activityInfo.packageName, packageManager)) { + DebugLogger.INSTANCE.logcatMessage(this, + () -> "getInfoThatResolvesIntent: ignoring resolved backlink app as it cannot" + + " start through launcher"); + return null; } - String requiredPackageName = requiredTaskInfo.getTopActivityPackageName(); - return resolvedComponent.getPackageName().equals(requiredPackageName); + Drawable appIcon = InternalBacklinksDataKt.getAppIcon(activityInfo, packageManager); + String appName = InternalBacklinksDataKt.getAppName(activityInfo, packageManager); + return new BacklinkDisplayInfo(appIcon, appName); + } + + /** + * Returns whether the app represented by the provided {@code pkgName} can be launched through + * the all apps tray by the user. + */ + private static boolean canAppStartThroughLauncher(String pkgName, PackageManager pkgManager) { + // Use Intent.resolveActivity API to check if the intent resolves as that is what Android + // uses internally when apps use Context.startActivity. + return getMainLauncherIntentForTask(pkgName, pkgManager) + .resolveActivity(pkgManager) != null; } - private Intent getMainLauncherIntentForTask(InternalTaskInfo internalTaskInfo) { - String pkgName = internalTaskInfo.getTopActivityPackageName(); + private static Intent getMainLauncherIntentForTask(String pkgName, + PackageManager packageManager) { Intent intent = new Intent(ACTION_MAIN).addCategory(CATEGORY_LAUNCHER).setPackage(pkgName); // Not all apps use DEFAULT_CATEGORY for their main launcher activity so the exact component // needs to be queried and set on the Intent in order for note-taking apps to be able to // start this intent. When starting an activity with an implicit intent, Android adds the // DEFAULT_CATEGORY flag otherwise it fails to resolve the intent. - PackageManager packageManager = internalTaskInfo.getPackageManager(); ResolveInfo resolvedActivity = packageManager.resolveActivity(intent, /* flags= */ 0); if (resolvedActivity != null) { intent.setComponent(resolvedActivity.getComponentInfo().getComponentName()); @@ -460,17 +519,6 @@ final class AppClipsViewModel extends ViewModel { return intent; } - private PackageManager getPackageManagerForUser(int userId) { - // If app clips was launched as the same user, then reuse the available PM from mContext. - if (mContext.getUserId() == userId) { - return mContext.getPackageManager(); - } - - // PackageManager required for a different user, create its context and return its PM. - UserHandle userHandle = UserHandle.of(userId); - return mContext.createContextAsUser(userHandle, /* flags= */ 0).getPackageManager(); - } - /** Helper factory to help with injecting {@link AppClipsViewModel}. */ static final class Factory implements ViewModelProvider.Factory { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/InternalBacklinksData.kt b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/InternalBacklinksData.kt index 234692ea2fc6..f4faa36ef718 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/InternalBacklinksData.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/InternalBacklinksData.kt @@ -27,16 +27,27 @@ import android.graphics.drawable.Drawable * represent error when necessary. */ internal sealed class InternalBacklinksData( - open val appIcon: Drawable, - open var displayLabel: String + // Fields from this object are made accessible through accessors to keep call sites simpler. + private val backlinkDisplayInfo: BacklinkDisplayInfo, ) { - data class BacklinksData(val clipData: ClipData, override val appIcon: Drawable) : - InternalBacklinksData(appIcon, clipData.description.label.toString()) + // Use separate field to access display label so that callers don't have to access through + // internal object. + var displayLabel: String + get() = backlinkDisplayInfo.displayLabel + set(value) { + backlinkDisplayInfo.displayLabel = value + } - data class CrossProfileError( - override val appIcon: Drawable, - override var displayLabel: String - ) : InternalBacklinksData(appIcon, displayLabel) + // Use separate field to access app icon so that callers don't have to access through internal + // object. + val appIcon: Drawable + get() = backlinkDisplayInfo.appIcon + + data class BacklinksData(val clipData: ClipData, private val icon: Drawable) : + InternalBacklinksData(BacklinkDisplayInfo(icon, clipData.description.label.toString())) + + data class CrossProfileError(private val icon: Drawable, private var label: String) : + InternalBacklinksData(BacklinkDisplayInfo(icon, label)) } /** @@ -54,11 +65,16 @@ internal data class InternalTaskInfo( val userId: Int, val packageManager: PackageManager ) { - fun getTopActivityNameForDebugLogging(): String = topActivityInfo.name + val topActivityNameForDebugLogging: String = topActivityInfo.name + val topActivityPackageName: String = topActivityInfo.packageName + val topActivityAppName: String by lazy { topActivityInfo.getAppName(packageManager) } + val topActivityAppIcon: Drawable by lazy { topActivityInfo.loadIcon(packageManager) } +} - fun getTopActivityPackageName(): String = topActivityInfo.packageName +internal fun ActivityInfo.getAppName(packageManager: PackageManager) = + loadLabel(packageManager).toString() - fun getTopActivityAppName(): String = topActivityInfo.loadLabel(packageManager).toString() +internal fun ActivityInfo.getAppIcon(packageManager: PackageManager) = loadIcon(packageManager) - fun getTopActivityAppIcon(): Drawable = topActivityInfo.loadIcon(packageManager) -} +/** A class to hold data that is used for displaying backlink to user in SysUI activity. */ +internal data class BacklinkDisplayInfo(val appIcon: Drawable, var displayLabel: String) diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java index 5d71c054244a..886b32b09225 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java @@ -24,6 +24,7 @@ import static android.content.Intent.ACTION_MAIN; import static android.content.Intent.ACTION_VIEW; import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_FAILED; import static android.content.Intent.CATEGORY_LAUNCHER; +import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY; import static android.view.Display.DEFAULT_DISPLAY; import static com.google.common.truth.Truth.assertThat; @@ -32,6 +33,7 @@ import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.reset; @@ -73,6 +75,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -108,19 +111,24 @@ public final class AppClipsViewModelTest extends SysuiTestCase { Context mMockedContext; @Mock private PackageManager mPackageManager; - private ArgumentCaptor<Intent> mPackageManagerIntentCaptor; + private ArgumentCaptor<Intent> mPackageManagerLauncherIntentCaptor; + private ArgumentCaptor<Intent> mPackageManagerBacklinkIntentCaptor; private AppClipsViewModel mViewModel; @Before public void setUp() throws RemoteException { MockitoAnnotations.initMocks(this); - mPackageManagerIntentCaptor = ArgumentCaptor.forClass(Intent.class); + mPackageManagerLauncherIntentCaptor = ArgumentCaptor.forClass(Intent.class); + mPackageManagerBacklinkIntentCaptor = ArgumentCaptor.forClass(Intent.class); // Set up mocking for backlinks. when(mAtmService.getTasks(Integer.MAX_VALUE, false, false, DEFAULT_DISPLAY)) .thenReturn(List.of(createTaskInfoForBacklinksTask())); - when(mPackageManager.resolveActivity(mPackageManagerIntentCaptor.capture(), anyInt())) - .thenReturn(createBacklinksTaskResolveInfo()); + ResolveInfo expectedResolveInfo = createBacklinksTaskResolveInfo(); + when(mPackageManager.resolveActivity(mPackageManagerLauncherIntentCaptor.capture(), + anyInt())).thenReturn(expectedResolveInfo); + when(mPackageManager.queryIntentActivities(mPackageManagerBacklinkIntentCaptor.capture(), + eq(MATCH_DEFAULT_ONLY))).thenReturn(List.of(expectedResolveInfo)); when(mPackageManager.loadItemIcon(any(), any())).thenReturn(FAKE_DRAWABLE); when(mMockedContext.getPackageManager()).thenReturn(mPackageManager); @@ -209,7 +217,7 @@ public final class AppClipsViewModelTest extends SysuiTestCase { mViewModel.triggerBacklinks(Collections.emptySet(), DEFAULT_DISPLAY); waitForIdleSync(); - Intent queriedIntent = mPackageManagerIntentCaptor.getValue(); + Intent queriedIntent = mPackageManagerBacklinkIntentCaptor.getValue(); assertThat(queriedIntent.getData()).isEqualTo(expectedUri); assertThat(queriedIntent.getAction()).isEqualTo(ACTION_VIEW); @@ -226,6 +234,63 @@ public final class AppClipsViewModelTest extends SysuiTestCase { } @Test + public void triggerBacklinks_shouldUpdateBacklinks_withUriForDifferentApp() { + Uri expectedUri = Uri.parse("https://android.com"); + AssistContent contentWithUri = new AssistContent(); + contentWithUri.setWebUri(expectedUri); + mockForAssistContent(contentWithUri, BACKLINKS_TASK_ID); + + // Reset PackageManager mocking done in setup. + reset(mPackageManager); + String package2 = BACKLINKS_TASK_PACKAGE_NAME + 2; + String appName2 = BACKLINKS_TASK_APP_NAME + 2; + ResolveInfo resolveInfo2 = createBacklinksTaskResolveInfo(); + ActivityInfo activityInfo2 = resolveInfo2.activityInfo; + activityInfo2.name = appName2; + activityInfo2.packageName = package2; + activityInfo2.applicationInfo.packageName = package2; + + Intent app2LauncherIntent = new Intent(ACTION_MAIN).addCategory( + CATEGORY_LAUNCHER).setPackage(package2); + when(mPackageManager.resolveActivity(intentEquals(app2LauncherIntent), eq(/* flags= */ 0))) + .thenReturn(resolveInfo2); + Intent uriIntent = new Intent(ACTION_VIEW).setData(expectedUri); + when(mPackageManager.queryIntentActivities(intentEquals(uriIntent), eq(MATCH_DEFAULT_ONLY))) + .thenReturn(List.of(resolveInfo2)); + when(mPackageManager.loadItemIcon(any(), any())).thenReturn(FAKE_DRAWABLE); + + mViewModel.triggerBacklinks(Collections.emptySet(), DEFAULT_DISPLAY); + waitForIdleSync(); + + BacklinksData result = (BacklinksData) mViewModel.mSelectedBacklinksLiveData.getValue(); + ClipData clipData = result.getClipData(); + ClipDescription resultDescription = clipData.getDescription(); + assertThat(resultDescription.getLabel().toString()).isEqualTo(appName2); + assertThat(resultDescription.getMimeType(0)).isEqualTo(MIMETYPE_TEXT_URILIST); + assertThat(clipData.getItemCount()).isEqualTo(1); + assertThat(clipData.getItemAt(0).getUri()).isEqualTo(expectedUri); + + assertThat(mViewModel.getBacklinksLiveData().getValue().size()).isEqualTo(1); + } + + private static class IntentMatcher implements ArgumentMatcher<Intent> { + private final Intent mExpectedIntent; + + IntentMatcher(Intent expectedIntent) { + mExpectedIntent = expectedIntent; + } + + @Override + public boolean matches(Intent actualIntent) { + return actualIntent != null && mExpectedIntent.filterEquals(actualIntent); + } + } + + private static Intent intentEquals(Intent intent) { + return argThat(new IntentMatcher(intent)); + } + + @Test public void triggerBacklinks_withNonResolvableUri_usesMainLauncherIntent() { Uri expectedUri = Uri.parse("https://developers.android.com"); AssistContent contentWithUri = new AssistContent(); @@ -249,7 +314,7 @@ public final class AppClipsViewModelTest extends SysuiTestCase { mViewModel.triggerBacklinks(Collections.emptySet(), DEFAULT_DISPLAY); waitForIdleSync(); - Intent queriedIntent = mPackageManagerIntentCaptor.getValue(); + Intent queriedIntent = mPackageManagerBacklinkIntentCaptor.getValue(); assertThat(queriedIntent.getPackage()).isEqualTo(expectedIntent.getPackage()); BacklinksData result = (BacklinksData) mViewModel.mSelectedBacklinksLiveData.getValue(); @@ -283,7 +348,7 @@ public final class AppClipsViewModelTest extends SysuiTestCase { mViewModel.triggerBacklinks(Collections.emptySet(), DEFAULT_DISPLAY); waitForIdleSync(); - Intent queriedIntent = mPackageManagerIntentCaptor.getValue(); + Intent queriedIntent = mPackageManagerLauncherIntentCaptor.getValue(); assertThat(queriedIntent.getPackage()).isEqualTo(BACKLINKS_TASK_PACKAGE_NAME); assertThat(queriedIntent.getAction()).isEqualTo(ACTION_MAIN); assertThat(queriedIntent.getCategories()).containsExactly(CATEGORY_LAUNCHER); @@ -356,7 +421,9 @@ public final class AppClipsViewModelTest extends SysuiTestCase { // For each task, the logic queries PM 3 times, twice for verifying if an app can be // launched via launcher and once with the data provided in backlink intent. when(mPackageManager.resolveActivity(any(), anyInt())).thenReturn(resolveInfo1, - resolveInfo1, resolveInfo1, resolveInfo2, resolveInfo2, resolveInfo2); + resolveInfo1, resolveInfo2, resolveInfo2); + when(mPackageManager.queryIntentActivities(any(Intent.class), eq(MATCH_DEFAULT_ONLY))) + .thenReturn(List.of(resolveInfo1)).thenReturn(List.of(resolveInfo2)); when(mPackageManager.loadItemIcon(any(), any())).thenReturn(FAKE_DRAWABLE); when(mAtmService.getTasks(Integer.MAX_VALUE, false, false, DEFAULT_DISPLAY)) .thenReturn(List.of(runningTaskInfo1, runningTaskInfo2)); diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt index 251a6b10a0da..80f6fc24ef2c 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt @@ -19,6 +19,7 @@ package com.android.systemui.education.domain.interactor import android.hardware.input.InputManager import com.android.systemui.education.data.repository.fakeEduClock import com.android.systemui.inputdevice.data.repository.UserInputDeviceRepository +import com.android.systemui.inputdevice.tutorial.tutorialSchedulerRepository import com.android.systemui.keyboard.data.repository.keyboardRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testDispatcher @@ -57,6 +58,8 @@ var Kosmos.keyboardTouchpadEduStatsInteractor by keyboardRepository, touchpadRepository, userRepository - ) + ), + tutorialSchedulerRepository, + fakeEduClock ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/inputdevice/tutorial/InputDeviceTutorialKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/inputdevice/tutorial/InputDeviceTutorialKosmos.kt index 827f0d277d11..a83baffd78b2 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/inputdevice/tutorial/InputDeviceTutorialKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/inputdevice/tutorial/InputDeviceTutorialKosmos.kt @@ -16,8 +16,20 @@ package com.android.systemui.inputdevice.tutorial +import android.content.applicationContext +import com.android.systemui.inputdevice.tutorial.data.repository.TutorialSchedulerRepository import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope import org.mockito.kotlin.mock var Kosmos.inputDeviceTutorialLogger: InputDeviceTutorialLogger by Kosmos.Fixture { mock<InputDeviceTutorialLogger>() } + +var Kosmos.tutorialSchedulerRepository by + Kosmos.Fixture { + TutorialSchedulerRepository( + applicationContext = applicationContext, + testScope.backgroundScope, + "KosmosTutorialSchedulerRepository" + ) + } diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 93b228f37c26..4898f1095c58 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -2390,8 +2390,8 @@ public final class ProcessList { } String volumeUuid = packageState.getVolumeUuid(); long inode = packageState.getUserStateOrDefault(userId).getCeDataInode(); - if (inode == 0) { - Slog.w(TAG, packageName + " inode == 0 (b/152760674)"); + if (inode <= 0) { + Slog.w(TAG, packageName + " inode == 0 or app uninstalled with keep-data"); return null; } result.put(packageName, Pair.create(volumeUuid, inode)); diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java index dba6c3372ae7..5d850896d5de 100644 --- a/services/core/java/com/android/server/biometrics/AuthService.java +++ b/services/core/java/com/android/server/biometrics/AuthService.java @@ -850,10 +850,28 @@ public class AuthService extends SystemService { return; } + boolean tempResetLockoutRequiresChallenge = false; + + if (hidlConfigStrings != null && hidlConfigStrings.length > 0) { + for (String configString : hidlConfigStrings) { + try { + SensorConfig sensor = new SensorConfig(configString); + switch (sensor.modality) { + case BiometricAuthenticator.TYPE_FACE: + tempResetLockoutRequiresChallenge = true; + break; + } + } catch (Exception e) { + Slog.e(TAG, "Error parsing configString: " + configString, e); + } + } + } + + final boolean resetLockoutRequiresChallenge = tempResetLockoutRequiresChallenge; + handlerProvider.getFaceHandler().post(() -> { final FaceSensorConfigurations mFaceSensorConfigurations = - new FaceSensorConfigurations(hidlConfigStrings != null - && hidlConfigStrings.length > 0); + new FaceSensorConfigurations(resetLockoutRequiresChallenge); if (hidlConfigStrings != null && hidlConfigStrings.length > 0) { mFaceSensorConfigurations.addHidlConfigs(hidlConfigStrings, context); diff --git a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java index 291f0e38908e..e5b990eeda4b 100644 --- a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java +++ b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java @@ -432,9 +432,6 @@ public abstract class PowerStatsCollector { EnergyConsumerResult[] energy = mConsumedEnergyRetriever.getConsumedEnergy(mEnergyConsumerIds); - System.out.println("mEnergyConsumerIds = " + Arrays.toString(mEnergyConsumerIds) + " " - + "energy = " - + Arrays.toString(energy)); if (energy == null) { return false; } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index ff6f021f52a7..1dfa06300ace 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -6079,9 +6079,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return false; } - // Check if the activity is on a sleeping display, canTurnScreenOn will also check - // keyguard visibility - if (mDisplayContent.isSleeping()) { + // Check if the activity is on a sleeping display and keyguard is not going away (to + // align with TaskFragment#shouldSleepActivities), canTurnScreenOn will also check keyguard + // visibility + if (mDisplayContent.isSleeping() && !mDisplayContent.isKeyguardGoingAway()) { return canTurnScreenOn(); } else { return mTaskSupervisor.getKeyguardController().checkKeyguardVisibility(this); diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/GameActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/GameActivity.java index ef75d4ddcdcd..93254f7247b6 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/GameActivity.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/GameActivity.java @@ -71,7 +71,7 @@ public class GameActivity extends Activity implements SurfaceHolder.Callback { windowInsetsController.setSystemBarsBehavior( WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE ); - // Hide both the status bar and the navigation bar. - windowInsetsController.hide(WindowInsetsCompat.Type.systemBars()); + // Hide status bar only to avoid flakiness on gesture quick switch cases. + windowInsetsController.hide(WindowInsetsCompat.Type.statusBars()); } } |