diff options
3 files changed, 116 insertions, 45 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index bb572191fe3c..c5639ea6fcde 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -84,6 +84,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.Dumpable; +import com.android.systemui.bubbles.animation.StackAnimationController; import com.android.systemui.bubbles.dagger.BubbleModule; import com.android.systemui.dump.DumpManager; import com.android.systemui.model.SysUiState; @@ -173,6 +174,12 @@ public class BubbleController implements ConfigurationController.ConfigurationLi @Nullable private BubbleStackView mStackView; private BubbleIconFactory mBubbleIconFactory; + /** + * The relative position of the stack when we removed it and nulled it out. If the stack is + * re-created, it will re-appear at this position. + */ + @Nullable private BubbleStackView.RelativeStackPosition mPositionFromRemovedStack; + // Tracks the id of the current (foreground) user. private int mCurrentUserId; // Saves notification keys of active bubbles when users are switched. @@ -736,6 +743,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi mContext, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator, mSysUiState, this::onAllBubblesAnimatedOut, this::onImeVisibilityChanged, this::hideCurrentInputMethod); + mStackView.setStackStartPosition(mPositionFromRemovedStack); mStackView.addView(mBubbleScrim); if (mExpandListener != null) { mStackView.setExpandListener(mExpandListener); @@ -806,6 +814,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi try { mAddedToWindowManager = false; if (mStackView != null) { + mPositionFromRemovedStack = mStackView.getRelativeStackPosition(); mWindowManager.removeView(mStackView); mStackView.removeView(mBubbleScrim); mStackView = null; diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index b3fbfd6a0f30..ec9644af7013 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -253,13 +253,8 @@ public class BubbleStackView extends FrameLayout /** Layout change listener that moves the stack to the nearest valid position on rotation. */ private OnLayoutChangeListener mOrientationChangedListener; - /** Whether the stack was on the left side of the screen prior to rotation. */ - private boolean mWasOnLeftBeforeRotation = false; - /** - * How far down the screen the stack was before rotation, in terms of percentage of the way down - * the allowable region. Defaults to -1 if not set. - */ - private float mVerticalPosPercentBeforeRotation = -1; + + @Nullable private RelativeStackPosition mRelativeStackPositionBeforeRotation; private int mMaxBubbles; private int mBubbleSize; @@ -940,9 +935,10 @@ public class BubbleStackView extends FrameLayout mExpandedViewContainer.setTranslationY(getExpandedViewY()); mExpandedViewContainer.setAlpha(1f); } - if (mVerticalPosPercentBeforeRotation >= 0) { - mStackAnimationController.moveStackToSimilarPositionAfterRotation( - mWasOnLeftBeforeRotation, mVerticalPosPercentBeforeRotation); + if (mRelativeStackPositionBeforeRotation != null) { + mStackAnimationController.setStackPosition( + mRelativeStackPositionBeforeRotation); + mRelativeStackPositionBeforeRotation = null; } removeOnLayoutChangeListener(mOrientationChangedListener); }; @@ -1189,13 +1185,7 @@ public class BubbleStackView extends FrameLayout com.android.internal.R.dimen.status_bar_height); mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top); - final RectF allowablePos = mStackAnimationController.getAllowableStackPositionRegion(); - mWasOnLeftBeforeRotation = mStackAnimationController.isStackOnLeftSide(); - mVerticalPosPercentBeforeRotation = - (mStackAnimationController.getStackPosition().y - allowablePos.top) - / (allowablePos.bottom - allowablePos.top); - mVerticalPosPercentBeforeRotation = - Math.max(0f, Math.min(1f, mVerticalPosPercentBeforeRotation)); + mRelativeStackPositionBeforeRotation = mStackAnimationController.getRelativeStackPosition(); addOnLayoutChangeListener(mOrientationChangedListener); hideFlyoutImmediate(); @@ -1459,7 +1449,7 @@ public class BubbleStackView extends FrameLayout if (getBubbleCount() == 0 && mShouldShowUserEducation) { // Override the default stack position if we're showing user education. mStackAnimationController.setStackPosition( - mStackAnimationController.getDefaultStartPosition()); + mStackAnimationController.getStartPosition()); } if (getBubbleCount() == 0) { @@ -1674,7 +1664,7 @@ public class BubbleStackView extends FrameLayout // Post so we have height of mUserEducationView mUserEducationView.post(() -> { final int viewHeight = mUserEducationView.getHeight(); - PointF stackPosition = mStackAnimationController.getDefaultStartPosition(); + PointF stackPosition = mStackAnimationController.getStartPosition(); final float translationY = stackPosition.y + (mBubbleSize / 2) - (viewHeight / 2); mUserEducationView.setTranslationY(translationY); mUserEducationView.animate() @@ -2740,10 +2730,18 @@ public class BubbleStackView extends FrameLayout .floatValue(); } + public void setStackStartPosition(RelativeStackPosition position) { + mStackAnimationController.setStackStartPosition(position); + } + public PointF getStackPosition() { return mStackAnimationController.getStackPosition(); } + public RelativeStackPosition getRelativeStackPosition() { + return mStackAnimationController.getRelativeStackPosition(); + } + /** * Logs the bubble UI event. * @@ -2797,4 +2795,47 @@ public class BubbleStackView extends FrameLayout } return bubbles; } + + /** + * Representation of stack position that uses relative properties rather than absolute + * coordinates. This is used to maintain similar stack positions across configuration changes. + */ + public static class RelativeStackPosition { + /** Whether to place the stack at the leftmost allowed position. */ + private boolean mOnLeft; + + /** + * How far down the vertically allowed region to place the stack. For example, if the stack + * allowed region is between y = 100 and y = 1100 and this is 0.2f, we'll place the stack at + * 100 + (0.2f * 1000) = 300. + */ + private float mVerticalOffsetPercent; + + public RelativeStackPosition(boolean onLeft, float verticalOffsetPercent) { + mOnLeft = onLeft; + mVerticalOffsetPercent = clampVerticalOffsetPercent(verticalOffsetPercent); + } + + /** Constructs a relative position given a region and a point in that region. */ + public RelativeStackPosition(PointF position, RectF region) { + mOnLeft = position.x < region.width() / 2; + mVerticalOffsetPercent = + clampVerticalOffsetPercent((position.y - region.top) / region.height()); + } + + /** Ensures that the offset percent is between 0f and 1f. */ + private float clampVerticalOffsetPercent(float offsetPercent) { + return Math.max(0f, Math.min(1f, offsetPercent)); + } + + /** + * Given an allowable stack position region, returns the point within that region + * represented by this relative position. + */ + public PointF getAbsolutePositionInRegion(RectF region) { + return new PointF( + mOnLeft ? region.left : region.right, + region.top + mVerticalOffsetPercent * region.height()); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java index b378469c4c98..e835ea206e59 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java @@ -35,6 +35,7 @@ import androidx.dynamicanimation.animation.SpringAnimation; import androidx.dynamicanimation.animation.SpringForce; import com.android.systemui.R; +import com.android.systemui.bubbles.BubbleStackView; import com.android.systemui.util.FloatingContentCoordinator; import com.android.systemui.util.animation.PhysicsAnimator; import com.android.systemui.util.magnetictarget.MagnetizedObject; @@ -125,6 +126,9 @@ public class StackAnimationController extends */ private Rect mAnimatingToBounds = new Rect(); + /** Initial starting location for the stack. */ + @Nullable private BubbleStackView.RelativeStackPosition mStackStartPosition; + /** Whether or not the stack's start position has been set. */ private boolean mStackMovedToStartPosition = false; @@ -431,21 +435,6 @@ public class StackAnimationController extends return stackPos; } - /** - * Moves the stack in response to rotation. We keep it in the most similar position by keeping - * it on the same side, and positioning it the same percentage of the way down the screen - * (taking status bar/nav bar into account by using the allowable region's height). - */ - public void moveStackToSimilarPositionAfterRotation(boolean wasOnLeft, float verticalPercent) { - final RectF allowablePos = getAllowableStackPositionRegion(); - final float allowableRegionHeight = allowablePos.bottom - allowablePos.top; - - final float x = wasOnLeft ? allowablePos.left : allowablePos.right; - final float y = (allowableRegionHeight * verticalPercent) + allowablePos.top; - - setStackPosition(new PointF(x, y)); - } - /** Description of current animation controller state. */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("StackAnimationController state:"); @@ -815,7 +804,7 @@ public class StackAnimationController extends } else { // When all children are removed ensure stack position is sane setStackPosition(mRestingStackPosition == null - ? getDefaultStartPosition() + ? getStartPosition() : mRestingStackPosition); // Remove the stack from the coordinator since we don't have any bubbles and aren't @@ -868,7 +857,7 @@ public class StackAnimationController extends mLayout.setVisibility(View.INVISIBLE); mLayout.post(() -> { setStackPosition(mRestingStackPosition == null - ? getDefaultStartPosition() + ? getStartPosition() : mRestingStackPosition); mStackMovedToStartPosition = true; mLayout.setVisibility(View.VISIBLE); @@ -938,15 +927,47 @@ public class StackAnimationController extends } } - /** Returns the default stack position, which is on the top left. */ - public PointF getDefaultStartPosition() { - boolean isRtl = mLayout != null - && mLayout.getResources().getConfiguration().getLayoutDirection() - == View.LAYOUT_DIRECTION_RTL; - return new PointF(isRtl - ? getAllowableStackPositionRegion().right - : getAllowableStackPositionRegion().left, - getAllowableStackPositionRegion().top + mStackStartingVerticalOffset); + public void setStackPosition(BubbleStackView.RelativeStackPosition position) { + setStackPosition(position.getAbsolutePositionInRegion(getAllowableStackPositionRegion())); + } + + public BubbleStackView.RelativeStackPosition getRelativeStackPosition() { + return new BubbleStackView.RelativeStackPosition( + mStackPosition, getAllowableStackPositionRegion()); + } + + /** + * Sets the starting position for the stack, where it will be located when the first bubble is + * added. + */ + public void setStackStartPosition(BubbleStackView.RelativeStackPosition position) { + mStackStartPosition = position; + } + + /** + * Returns the starting stack position. If {@link #setStackStartPosition} was called, this will + * return that position - otherwise, a reasonable default will be returned. + */ + @Nullable public PointF getStartPosition() { + if (mLayout == null) { + return null; + } + + if (mStackStartPosition == null) { + // Start on the left if we're in LTR, right otherwise. + final boolean startOnLeft = + mLayout.getResources().getConfiguration().getLayoutDirection() + != View.LAYOUT_DIRECTION_RTL; + + final float startingVerticalOffset = mLayout.getResources().getDimensionPixelOffset( + R.dimen.bubble_stack_starting_offset_y); + + mStackStartPosition = new BubbleStackView.RelativeStackPosition( + startOnLeft, + startingVerticalOffset / getAllowableStackPositionRegion().height()); + } + + return mStackStartPosition.getAbsolutePositionInRegion(getAllowableStackPositionRegion()); } private boolean isStackPositionSet() { |