diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 3f94b00d3c609..6e38d8826078f 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -177,6 +177,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config private ScrimView mBubbleScrim; @Nullable private BubbleStackView mStackView; private BubbleIconFactory mBubbleIconFactory; + private BubblePositioner mBubblePositioner; /** * The relative position of the stack when we removed it and nulled it out. If the stack is @@ -387,7 +388,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config dumpManager, floatingContentCoordinator, new BubbleDataRepository(context, launcherApps), sysUiState, notificationManager, statusBarService, windowManager, windowManagerShellWrapper, launcherApps, logger, - mainHandler, organizer); + mainHandler, organizer, new BubblePositioner(context, windowManager)); } /** @@ -419,7 +420,8 @@ public class BubbleController implements Bubbles, ConfigurationController.Config LauncherApps launcherApps, BubbleLogger bubbleLogger, Handler mainHandler, - ShellTaskOrganizer organizer) { + ShellTaskOrganizer organizer, + BubblePositioner positioner) { dumpManager.registerDumpable(TAG, this); mContext = context; mShadeController = shadeController; @@ -530,6 +532,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config mBubbleIconFactory = new BubbleIconFactory(context); mTaskListener = new MultiWindowTaskListener(mMainHandler, organizer); + mBubblePositioner = positioner; launcherApps.registerCallback(new LauncherApps.Callback() { @Override @@ -809,6 +812,11 @@ public class BubbleController implements Bubbles, ConfigurationController.Config return mTaskListener; } + @Override + public BubblePositioner getPositioner() { + return mBubblePositioner; + } + /** * BubbleStackView is lazily created by this method the first time a Bubble is added. This * method initializes the stack view and adds it to the StatusBar just above the scrim. @@ -818,9 +826,10 @@ public class BubbleController implements Bubbles, ConfigurationController.Config mStackView = new BubbleStackView( mContext, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator, this::onAllBubblesAnimatedOut, this::onImeVisibilityChanged, - this::hideCurrentInputMethod, this::onBubbleExpandChanged); + this::hideCurrentInputMethod, this::onBubbleExpandChanged, mBubblePositioner); mStackView.setStackStartPosition(mPositionFromRemovedStack); mStackView.addView(mBubbleScrim); + mStackView.onOrientationChanged(); if (mExpandListener != null) { mStackView.setExpandListener(mExpandListener); } @@ -978,10 +987,14 @@ public class BubbleController implements Bubbles, ConfigurationController.Config @Override public void onConfigChanged(Configuration newConfig) { + if (mBubblePositioner != null) { + // This doesn't trigger any changes, always update it + mBubblePositioner.update(newConfig.orientation); + } if (mStackView != null && newConfig != null) { if (newConfig.orientation != mOrientation) { mOrientation = newConfig.orientation; - mStackView.onOrientationChanged(newConfig.orientation); + mStackView.onOrientationChanged(); } if (newConfig.densityDpi != mDensityDpi) { mDensityDpi = newConfig.densityDpi; diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index 98a2257d2daaf..997805ad0433c 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -38,7 +38,6 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.Outline; -import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.ShapeDrawable; import android.os.Bundle; @@ -48,7 +47,6 @@ import android.view.SurfaceControl; import android.view.View; import android.view.ViewGroup; import android.view.ViewOutlineProvider; -import android.view.WindowManager; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; import android.widget.LinearLayout; @@ -83,24 +81,26 @@ public class BubbleExpandedView extends LinearLayout { private boolean mImeVisible; private boolean mNeedsNewHeight; - private Point mDisplaySize; private int mMinHeight; private int mOverflowHeight; private int mSettingsIconHeight; private int mPointerWidth; private int mPointerHeight; - private ShapeDrawable mPointerDrawable; + private ShapeDrawable mCurrentPointer; + private ShapeDrawable mTopPointer; + private ShapeDrawable mLeftPointer; + private ShapeDrawable mRightPointer; private int mExpandedViewPadding; private float mCornerRadius = 0f; @Nullable private Bubble mBubble; private PendingIntent mPendingIntent; - + // TODO(b/170891664): Don't use a flag, set the BubbleOverflow object instead private boolean mIsOverflow; private Bubbles mBubbles = Dependency.get(Bubbles.class); - private WindowManager mWindowManager; private BubbleStackView mStackView; + private BubblePositioner mPositioner; /** * Container for the ActivityView that has a solid, round-rect background that shows if the @@ -224,17 +224,6 @@ public class BubbleExpandedView extends LinearLayout { updateDimensions(); } - void updateDimensions() { - mDisplaySize = new Point(); - mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); - // Get the real size -- this includes screen decorations (notches, statusbar, navbar). - mWindowManager.getDefaultDisplay().getRealSize(mDisplaySize); - Resources res = getResources(); - mMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height); - mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height); - mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin); - } - @SuppressLint("ClickableViewAccessibility") @Override protected void onFinishInflate() { @@ -245,14 +234,22 @@ public class BubbleExpandedView extends LinearLayout { mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width); mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height); - mPointerDrawable = new ShapeDrawable(TriangleShape.create( + mTopPointer = new ShapeDrawable(TriangleShape.create( mPointerWidth, mPointerHeight, true /* pointUp */)); + mLeftPointer = new ShapeDrawable(TriangleShape.createHorizontal( + mPointerWidth, mPointerHeight, true /* pointLeft */)); + mRightPointer = new ShapeDrawable(TriangleShape.createHorizontal( + mPointerWidth, mPointerHeight, false /* pointLeft */)); + + mCurrentPointer = mTopPointer; mPointerView.setVisibility(INVISIBLE); mSettingsIconHeight = getContext().getResources().getDimensionPixelSize( R.dimen.bubble_manage_button_height); mSettingsIcon = findViewById(R.id.settings_button); + mPositioner = mBubbles.getPositioner(); + mTaskView = new TaskView(mContext, mBubbles.getTaskManager()); // Set ActivityView's alpha value as zero, since there is no view content to be shown. setContentVisibility(false); @@ -282,8 +279,7 @@ public class BubbleExpandedView extends LinearLayout { applyThemeAttrs(); mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding); - setPadding(mExpandedViewPadding, mExpandedViewPadding, mExpandedViewPadding, - mExpandedViewPadding); + setClipToPadding(false); setOnTouchListener((view, motionEvent) -> { if (mTaskView == null) { return false; @@ -311,6 +307,52 @@ public class BubbleExpandedView extends LinearLayout { setLayoutDirection(LAYOUT_DIRECTION_LOCALE); } + void updateDimensions() { + Resources res = getResources(); + mMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height); + mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height); + mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin); + } + + void applyThemeAttrs() { + final TypedArray ta = mContext.obtainStyledAttributes(new int[] { + android.R.attr.dialogCornerRadius, + android.R.attr.colorBackgroundFloating}); + mCornerRadius = ta.getDimensionPixelSize(0, 0); + mExpandedViewContainer.setBackgroundColor(ta.getColor(1, Color.WHITE)); + ta.recycle(); + + if (mTaskView != null && ScreenDecorationsUtils.supportsRoundedCornersOnWindows( + mContext.getResources())) { + mTaskView.setCornerRadius(mCornerRadius); + } + updatePointerView(); + } + + private void updatePointerView() { + final int mode = + getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; + switch (mode) { + case Configuration.UI_MODE_NIGHT_NO: + mCurrentPointer.setTint(getResources().getColor(R.color.bubbles_light)); + break; + case Configuration.UI_MODE_NIGHT_YES: + mCurrentPointer.setTint(getResources().getColor(R.color.bubbles_dark)); + break; + } + LayoutParams lp = (LayoutParams) mPointerView.getLayoutParams(); + if (mCurrentPointer == mLeftPointer || mCurrentPointer == mRightPointer) { + lp.width = mPointerHeight; + lp.height = mPointerWidth; + } else { + lp.width = mPointerWidth; + lp.height = mPointerHeight; + } + mPointerView.setLayoutParams(lp); + mPointerView.setBackground(mCurrentPointer); + } + + private String getBubbleKey() { return mBubble != null ? mBubble.getKey() : "null"; } @@ -371,32 +413,6 @@ public class BubbleExpandedView extends LinearLayout { } } - void applyThemeAttrs() { - final TypedArray ta = mContext.obtainStyledAttributes(new int[] { - android.R.attr.dialogCornerRadius, - android.R.attr.colorBackgroundFloating}); - mCornerRadius = ta.getDimensionPixelSize(0, 0); - mExpandedViewContainer.setBackgroundColor(ta.getColor(1, Color.WHITE)); - ta.recycle(); - - if (mTaskView != null && ScreenDecorationsUtils.supportsRoundedCornersOnWindows( - mContext.getResources())) { - mTaskView.setCornerRadius(mCornerRadius); - } - - final int mode = - getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; - switch (mode) { - case Configuration.UI_MODE_NIGHT_NO: - mPointerDrawable.setTint(getResources().getColor(R.color.bubbles_light)); - break; - case Configuration.UI_MODE_NIGHT_YES: - mPointerDrawable.setTint(getResources().getColor(R.color.bubbles_dark)); - break; - } - mPointerView.setBackground(mPointerDrawable); - } - @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); @@ -522,12 +538,12 @@ public class BubbleExpandedView extends LinearLayout { } if (mBubble != null || mIsOverflow) { - float desiredHeight = mOverflowHeight; - if (!mIsOverflow) { - desiredHeight = Math.max(mBubble.getDesiredHeight(mContext), mMinHeight); - } + float desiredHeight = mIsOverflow + ? mOverflowHeight + : mBubble.getDesiredHeight(mContext); + desiredHeight = Math.max(desiredHeight, mMinHeight); float height = Math.min(desiredHeight, getMaxExpandedHeight()); - height = Math.max(height, mIsOverflow ? mOverflowHeight : mMinHeight); + height = Math.max(height, mMinHeight); FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mTaskView.getLayoutParams(); mNeedsNewHeight = lp.height != height; if (!mImeVisible) { @@ -546,21 +562,17 @@ public class BubbleExpandedView extends LinearLayout { } private int getMaxExpandedHeight() { - mWindowManager.getDefaultDisplay().getRealSize(mDisplaySize); int expandedContainerY = mExpandedViewContainerLocation != null - ? mExpandedViewContainerLocation[1] + // Remove top insets back here because availableRect.height would account for that + ? mExpandedViewContainerLocation[1] - mPositioner.getInsets().top : 0; - int bottomInset = getRootWindowInsets() != null - ? getRootWindowInsets().getStableInsetBottom() - : 0; - - return mDisplaySize.y + return mPositioner.getAvailableRect().height() - expandedContainerY - getPaddingTop() - getPaddingBottom() - mSettingsIconHeight - mPointerHeight - - mPointerMargin - bottomInset; + - mPointerMargin; } /** @@ -585,12 +597,25 @@ public class BubbleExpandedView extends LinearLayout { } /** - * Set the x position that the tip of the triangle should point to. + * Set the position that the tip of the triangle should point to. */ - public void setPointerPosition(float x) { - float halfPointerWidth = mPointerWidth / 2f; - float pointerLeft = x - halfPointerWidth - mExpandedViewPadding; - mPointerView.setTranslationX(pointerLeft); + public void setPointerPosition(float x, float y, boolean isLandscape, boolean onLeft) { + // Pointer gets drawn in the padding + int paddingLeft = (isLandscape && onLeft) ? mPointerHeight : 0; + int paddingRight = (isLandscape && !onLeft) ? mPointerHeight : 0; + int paddingTop = isLandscape ? 0 : mExpandedViewPadding; + setPadding(paddingLeft, paddingTop, paddingRight, 0); + + if (isLandscape) { + // TODO: why setY vs setTranslationY ? linearlayout? + mPointerView.setY(y - (mPointerWidth / 2f)); + mPointerView.setTranslationX(onLeft ? -mPointerHeight : x - mExpandedViewPadding); + } else { + mPointerView.setTranslationY(0f); + mPointerView.setTranslationX(x - mExpandedViewPadding - (mPointerWidth / 2f)); + } + mCurrentPointer = isLandscape ? onLeft ? mLeftPointer : mRightPointer : mTopPointer; + updatePointerView(); mPointerView.setVisibility(VISIBLE); } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubblePositioner.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubblePositioner.java new file mode 100644 index 0000000000000..029caee6364ff --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubblePositioner.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.bubbles; + +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Insets; +import android.graphics.Rect; +import android.view.WindowInsets; +import android.view.WindowManager; +import android.view.WindowMetrics; + +import androidx.annotation.VisibleForTesting; + +/** + * Keeps track of display size, configuration, and specific bubble sizes. One place for all + * placement and positioning calculations to refer to. + */ +public class BubblePositioner { + + private WindowManager mWindowManager; + private Rect mPositionRect; + private int mOrientation; + private Insets mInsets; + + public BubblePositioner(Context context, WindowManager windowManager) { + mWindowManager = windowManager; + update(Configuration.ORIENTATION_UNDEFINED); + } + + public void update(int orientation) { + WindowMetrics windowMetrics = mWindowManager.getCurrentWindowMetrics(); + mPositionRect = new Rect(windowMetrics.getBounds()); + WindowInsets metricInsets = windowMetrics.getWindowInsets(); + + Insets insets = metricInsets.getInsetsIgnoringVisibility(WindowInsets.Type.navigationBars() + | WindowInsets.Type.statusBars() + | WindowInsets.Type.displayCutout()); + update(orientation, insets, windowMetrics.getBounds()); + } + + @VisibleForTesting + public void update(int orientation, Insets insets, Rect bounds) { + mOrientation = orientation; + mInsets = insets; + + mPositionRect = new Rect(bounds); + mPositionRect.left += mInsets.left; + mPositionRect.top += mInsets.top; + mPositionRect.right -= mInsets.right; + mPositionRect.bottom -= mInsets.bottom; + } + + /** + * @return a rect of available screen space for displaying bubbles in the correct orientation, + * accounting for system bars and cutouts. + */ + public Rect getAvailableRect() { + return mPositionRect; + } + + /** + * @return the current orientation. + */ + public int getOrientation() { + return mOrientation; + } + + /** + * @return the relevant insets (status bar, nav bar, cutouts). + */ + public Insets getInsets() { + return mInsets; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index 431719f98ad93..ae1145add2f99 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -35,9 +35,9 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; +import android.graphics.Insets; import android.graphics.Outline; import android.graphics.Paint; -import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; @@ -47,7 +47,6 @@ import android.os.Handler; import android.provider.Settings; import android.util.Log; import android.view.Choreographer; -import android.view.DisplayCutout; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.SurfaceControl; @@ -57,7 +56,6 @@ import android.view.ViewGroup; import android.view.ViewOutlineProvider; import android.view.ViewTreeObserver; import android.view.WindowInsets; -import android.view.WindowManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.animation.AccelerateDecelerateInterpolator; @@ -188,8 +186,6 @@ public class BubbleStackView extends FrameLayout } }; - private Point mDisplaySize; - private final BubbleData mBubbleData; private final ValueAnimator mDesaturateAndDarkenAnimator; @@ -245,8 +241,8 @@ public class BubbleStackView extends FrameLayout private int mBubblePaddingTop; private int mBubbleTouchPadding; private int mExpandedViewPadding; + private int mPointerHeight; private int mCornerRadius; - private int mStatusBarHeight; private int mImeOffset; @Nullable private BubbleViewProvider mExpandedBubble; private boolean mIsExpanded; @@ -721,13 +717,11 @@ public class BubbleStackView extends FrameLayout } }; - private DismissView mDismissView; - private int mOrientation = Configuration.ORIENTATION_UNDEFINED; - @Nullable private BubbleOverflow mBubbleOverflow; private StackEducationView mStackEduView; private ManageEducationView mManageEduView; + private DismissView mDismissView; private ViewGroup mManageMenu; private ImageView mManageSettingsIcon; @@ -735,6 +729,8 @@ public class BubbleStackView extends FrameLayout private boolean mShowingManage = false; private PhysicsAnimator.SpringConfig mManageSpringConfig = new PhysicsAnimator.SpringConfig( SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_LOW_BOUNCY); + private BubblePositioner mPositioner; + @SuppressLint("ClickableViewAccessibility") public BubbleStackView(Context context, BubbleData data, @Nullable SurfaceSynchronizer synchronizer, @@ -742,7 +738,8 @@ public class BubbleStackView extends FrameLayout Runnable allBubblesAnimatedOutAction, Consumer onImeVisibilityChanged, Runnable hideCurrentInputMethodCallback, - Consumer onBubbleExpandChanged) { + Consumer onBubbleExpandChanged, + BubblePositioner positioner) { super(context); mBubbleData = data; @@ -753,15 +750,11 @@ public class BubbleStackView extends FrameLayout mBubbleElevation = res.getDimensionPixelSize(R.dimen.bubble_elevation); mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top); mBubbleTouchPadding = res.getDimensionPixelSize(R.dimen.bubble_touch_padding); + mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height); - mStatusBarHeight = - res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset); - mDisplaySize = new Point(); - WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); - // We use the real size & subtract screen decorations / window insets ourselves when needed - wm.getDefaultDisplay().getRealSize(mDisplaySize); + mPositioner = positioner; mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding); int elevation = res.getDimensionPixelSize(R.dimen.bubble_elevation); @@ -778,11 +771,10 @@ public class BubbleStackView extends FrameLayout }; mStackAnimationController = new StackAnimationController( - floatingContentCoordinator, this::getBubbleCount, onBubbleAnimatedOut); + floatingContentCoordinator, this::getBubbleCount, onBubbleAnimatedOut, mPositioner); mExpandedAnimationController = new ExpandedAnimationController( - mDisplaySize, mExpandedViewPadding, res.getConfiguration().orientation, - onBubbleAnimatedOut); + mPositioner, mExpandedViewPadding, onBubbleAnimatedOut); mSurfaceSynchronizer = synchronizer != null ? synchronizer : DEFAULT_SURFACE_SYNCHRONIZER; // Force LTR by default since most of the Bubbles UI is positioned manually by the user, or @@ -817,10 +809,10 @@ public class BubbleStackView extends FrameLayout mAnimatingOutSurfaceContainer.addView(mAnimatingOutSurfaceView); mAnimatingOutSurfaceContainer.setPadding( - mExpandedViewPadding, - mExpandedViewPadding, - mExpandedViewPadding, - mExpandedViewPadding); + mExpandedViewContainer.getPaddingLeft(), + mExpandedViewContainer.getPaddingTop(), + mExpandedViewContainer.getPaddingRight(), + mExpandedViewContainer.getPaddingBottom()); setUpManageMenu(); @@ -871,29 +863,16 @@ public class BubbleStackView extends FrameLayout mOrientationChangedListener = (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { - mExpandedAnimationController.updateResources(mOrientation, mDisplaySize); - mStackAnimationController.updateResources(mOrientation); + onDisplaySizeChanged(); + mExpandedAnimationController.updateResources(); + mStackAnimationController.updateResources(); mBubbleOverflow.updateResources(); - // Need to update the padding around the view - WindowInsets insets = getRootWindowInsets(); - int leftPadding = mExpandedViewPadding; - int rightPadding = mExpandedViewPadding; - if (insets != null) { - // Can't have the expanded view overlaying notches - int cutoutLeft = 0; - int cutoutRight = 0; - DisplayCutout cutout = insets.getDisplayCutout(); - if (cutout != null) { - cutoutLeft = cutout.getSafeInsetLeft(); - cutoutRight = cutout.getSafeInsetRight(); - } - // Or overlaying nav or status bar - leftPadding += Math.max(cutoutLeft, insets.getStableInsetLeft()); - rightPadding += Math.max(cutoutRight, insets.getStableInsetRight()); + if (mRelativeStackPositionBeforeRotation != null) { + mStackAnimationController.setStackPosition( + mRelativeStackPositionBeforeRotation); + mRelativeStackPositionBeforeRotation = null; } - mExpandedViewContainer.setPadding(leftPadding, mExpandedViewPadding, - rightPadding, mExpandedViewPadding); if (mIsExpanded) { // Re-draw bubble row and pointer for new orientation. @@ -903,15 +882,10 @@ public class BubbleStackView extends FrameLayout mExpandedAnimationController.expandFromStack(() -> { afterExpandedViewAnimation(); } /* after */); - mExpandedViewContainer.setTranslationX(0); + mExpandedViewContainer.setTranslationX(0f); mExpandedViewContainer.setTranslationY(getExpandedViewY()); mExpandedViewContainer.setAlpha(1f); } - if (mRelativeStackPositionBeforeRotation != null) { - mStackAnimationController.setStackPosition( - mRelativeStackPositionBeforeRotation); - mRelativeStackPositionBeforeRotation = null; - } removeOnLayoutChangeListener(mOrientationChangedListener); }; @@ -1178,26 +1152,16 @@ public class BubbleStackView extends FrameLayout } /** Respond to the phone being rotated by repositioning the stack and hiding any flyouts. */ - public void onOrientationChanged(int orientation) { - mOrientation = orientation; - - // Display size is based on the rotation device was in when requested, we should update it - // We use the real size & subtract screen decorations / window insets ourselves when needed - WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); - wm.getDefaultDisplay().getRealSize(mDisplaySize); - - // Some resources change depending on orientation + public void onOrientationChanged() { Resources res = getContext().getResources(); - mStatusBarHeight = res.getDimensionPixelSize( - com.android.internal.R.dimen.status_bar_height); mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top); mRelativeStackPositionBeforeRotation = mStackAnimationController.getRelativeStackPosition(); - addOnLayoutChangeListener(mOrientationChangedListener); - hideFlyoutImmediate(); - mManageMenu.setVisibility(View.INVISIBLE); mShowingManage = false; + + addOnLayoutChangeListener(mOrientationChangedListener); + hideFlyoutImmediate(); } /** Tells the views with locale-dependent layout direction to resolve the new direction. */ @@ -1217,11 +1181,7 @@ public class BubbleStackView extends FrameLayout public void onDisplaySizeChanged() { updateOverflow(); - WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); - wm.getDefaultDisplay().getRealSize(mDisplaySize); Resources res = getContext().getResources(); - mStatusBarHeight = res.getDimensionPixelSize( - com.android.internal.R.dimen.status_bar_height); mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top); mBubbleSize = getResources().getDimensionPixelSize(R.dimen.individual_bubble_size); for (Bubble b : mBubbleData.getBubbles()) { @@ -1231,8 +1191,8 @@ public class BubbleStackView extends FrameLayout } b.getIconView().setLayoutParams(new LayoutParams(mBubbleSize, mBubbleSize)); } - mExpandedAnimationController.updateResources(mOrientation, mDisplaySize); - mStackAnimationController.updateResources(mOrientation); + mExpandedAnimationController.updateResources(); + mStackAnimationController.updateResources(); mDismissView.updateResources(); mMagneticTarget.setMagneticFieldRadiusPx(mBubbleSize * 2); } @@ -1682,7 +1642,8 @@ public class BubbleStackView extends FrameLayout private void animateExpansion() { cancelDelayedExpandCollapseSwitchAnimations(); - + final boolean isLandscape = + mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE; mIsExpanded = true; if (mStackEduView != null) { mStackEduView.hide(true /* fromExpansion */); @@ -1698,20 +1659,21 @@ public class BubbleStackView extends FrameLayout } } /* after */); - mExpandedViewContainer.setTranslationX(0); + mExpandedViewContainer.setTranslationX(0f); mExpandedViewContainer.setTranslationY(getExpandedViewY()); mExpandedViewContainer.setAlpha(1f); // X-value of the bubble we're expanding, once it's settled in its row. - final float bubbleWillBeAtX = - mExpandedAnimationController.getBubbleLeft( + final float bubbleWillBeAt = + mExpandedAnimationController.getBubbleXOrYForOrientation( mBubbleData.getBubbles().indexOf(mExpandedBubble)); // How far horizontally the bubble will be animating. We'll wait a bit longer for bubbles // that are animating farther, so that the expanded view doesn't move as much. - final float horizontalDistanceAnimated = - Math.abs(bubbleWillBeAtX - - mStackAnimationController.getStackPosition().x); + final float relevantStackPosition = isLandscape + ? mStackAnimationController.getStackPosition().y + : mStackAnimationController.getStackPosition().x; + final float distanceAnimated = Math.abs(bubbleWillBeAt - relevantStackPosition); // Wait for the path animation target to reach its end, and add a small amount of extra time // if the bubble is moving a lot horizontally. @@ -1721,13 +1683,26 @@ public class BubbleStackView extends FrameLayout if (getWidth() > 0) { startDelay = (long) (ExpandedAnimationController.EXPAND_COLLAPSE_TARGET_ANIM_DURATION - + (horizontalDistanceAnimated / getWidth()) * 30); + + (distanceAnimated / getWidth()) * 30); } // Set the pivot point for the scale, so the expanded view animates out from the bubble. - mExpandedViewContainerMatrix.setScale( - 0f, 0f, - bubbleWillBeAtX + mBubbleSize / 2f, getExpandedViewY()); + if (isLandscape) { + float pivotX; + float pivotY = bubbleWillBeAt + mBubbleSize / 2f; + if (mStackOnLeftOrWillBe) { + pivotX = mPositioner.getAvailableRect().left + mBubbleSize + mExpandedViewPadding; + } else { + pivotX = mPositioner.getAvailableRect().right - mBubbleSize - mExpandedViewPadding; + } + mExpandedViewContainerMatrix.setScale( + 0f, 0f, + pivotX, pivotY); + } else { + mExpandedViewContainerMatrix.setScale( + 0f, 0f, + bubbleWillBeAt + mBubbleSize / 2f, getExpandedViewY()); + } mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix); if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) { @@ -1747,9 +1722,11 @@ public class BubbleStackView extends FrameLayout if (mExpandedBubble == null || mExpandedBubble.getIconView() == null) { return; } + float translation = isLandscape + ? mExpandedBubble.getIconView().getTranslationY() + : mExpandedBubble.getIconView().getTranslationX(); mExpandedViewContainerMatrix.postTranslate( - mExpandedBubble.getIconView().getTranslationX() - - bubbleWillBeAtX, + translation - bubbleWillBeAt, 0); mExpandedViewContainer.setAnimationMatrix( mExpandedViewContainerMatrix); @@ -1797,16 +1774,29 @@ public class BubbleStackView extends FrameLayout // We want to visually collapse into this bubble during the animation. final View expandingFromBubble = mExpandedBubble.getIconView(); - // X-value the bubble is animating from (back into the stack). - final float expandingFromBubbleAtX = - mExpandedAnimationController.getBubbleLeft( + // Value the bubble is animating from (back into the stack). + final float expandingFromBubbleAt = + mExpandedAnimationController.getBubbleXOrYForOrientation( mBubbleData.getBubbles().indexOf(mExpandedBubble)); - - // Set the pivot point. - mExpandedViewContainerMatrix.setScale( - 1f, 1f, - expandingFromBubbleAtX + mBubbleSize / 2f, - getExpandedViewY()); + final boolean isLandscape = + mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE; + if (isLandscape) { + float pivotX; + float pivotY = expandingFromBubbleAt + mBubbleSize / 2f; + if (mStackOnLeftOrWillBe) { + pivotX = mPositioner.getAvailableRect().left + mBubbleSize + mExpandedViewPadding; + } else { + pivotX = mPositioner.getAvailableRect().right - mBubbleSize - mExpandedViewPadding; + } + mExpandedViewContainerMatrix.setScale( + 1f, 1f, + pivotX, pivotY); + } else { + mExpandedViewContainerMatrix.setScale( + 1f, 1f, + expandingFromBubbleAt + mBubbleSize / 2f, + getExpandedViewY()); + } PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel(); PhysicsAnimator.getInstance(mExpandedViewContainerMatrix) @@ -1815,9 +1805,15 @@ public class BubbleStackView extends FrameLayout .addUpdateListener((target, values) -> { if (expandingFromBubble != null) { // Follow the bubble as it translates! - mExpandedViewContainerMatrix.postTranslate( - expandingFromBubble.getTranslationX() - - expandingFromBubbleAtX, 0f); + if (isLandscape) { + mExpandedViewContainerMatrix.postTranslate( + 0f, expandingFromBubble.getTranslationY() + - expandingFromBubbleAt); + } else { + mExpandedViewContainerMatrix.postTranslate( + expandingFromBubble.getTranslationX() + - expandingFromBubbleAt, 0f); + } } mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix); @@ -1861,26 +1857,55 @@ public class BubbleStackView extends FrameLayout // The surface contains a screenshot of the animating out bubble, so we just need to animate // it out (and then release the GraphicBuffer). PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer).cancel(); - PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer) + PhysicsAnimator animator = PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer) .spring(DynamicAnimation.SCALE_X, 0f, mScaleOutSpringConfig) .spring(DynamicAnimation.SCALE_Y, 0f, mScaleOutSpringConfig) - .spring(DynamicAnimation.TRANSLATION_Y, - mAnimatingOutSurfaceContainer.getTranslationY() - mBubbleSize * 2, - mTranslateSpringConfig) - .withEndActions(this::releaseAnimatingOutBubbleBuffer) - .start(); + .withEndActions(this::releaseAnimatingOutBubbleBuffer); + + if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) { + float translationX = mStackAnimationController.isStackOnLeftSide() + ? mAnimatingOutSurfaceContainer.getTranslationX() + mBubbleSize * 2 + : mAnimatingOutSurfaceContainer.getTranslationX(); + animator.spring(DynamicAnimation.TRANSLATION_X, + translationX, + mTranslateSpringConfig) + .start(); + } else { + animator.spring(DynamicAnimation.TRANSLATION_Y, + mAnimatingOutSurfaceContainer.getTranslationY() - mBubbleSize * 2, + mTranslateSpringConfig) + .start(); + } boolean isOverflow = mExpandedBubble != null && mExpandedBubble.getKey().equals(BubbleOverflow.KEY); - float expandingFromBubbleDestinationX = - mExpandedAnimationController.getBubbleLeft(isOverflow ? getBubbleCount() + float expandingFromBubbleDestination = + mExpandedAnimationController.getBubbleXOrYForOrientation(isOverflow + ? getBubbleCount() : mBubbleData.getBubbles().indexOf(mExpandedBubble)); mExpandedViewContainer.setAlpha(1f); mExpandedViewContainer.setVisibility(View.VISIBLE); - mExpandedViewContainerMatrix.setScale( - 0f, 0f, expandingFromBubbleDestinationX + mBubbleSize / 2f, getExpandedViewY()); + if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) { + float pivotX; + float pivotY = expandingFromBubbleDestination + mBubbleSize / 2f; + if (mStackOnLeftOrWillBe) { + pivotX = mPositioner.getAvailableRect().left + mBubbleSize + mExpandedViewPadding; + } else { + pivotX = mPositioner.getAvailableRect().right - mBubbleSize - mExpandedViewPadding; + + } + mExpandedViewContainerMatrix.setScale( + 0f, 0f, + pivotX, pivotY); + } else { + mExpandedViewContainerMatrix.setScale( + 0f, 0f, + expandingFromBubbleDestination + mBubbleSize / 2f, + getExpandedViewY()); + } + mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix); mDelayedAnimationHandler.postDelayed(() -> { @@ -2168,7 +2193,12 @@ public class BubbleStackView extends FrameLayout * Calculates the y position of the expanded view when it is expanded. */ float getExpandedViewY() { - return getStatusBarHeight() + mBubbleSize + mBubblePaddingTop; + final int top = mPositioner.getAvailableRect().top; + if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) { + return top + mExpandedViewPadding; + } else { + return top + mBubbleSize + mBubblePaddingTop; + } } private boolean shouldShowFlyout(Bubble bubble) { @@ -2323,19 +2353,6 @@ public class BubbleStackView extends FrameLayout } } - private int getStatusBarHeight() { - if (getRootWindowInsets() != null) { - WindowInsets insets = getRootWindowInsets(); - return Math.max( - mStatusBarHeight, - insets.getDisplayCutout() != null - ? insets.getDisplayCutout().getSafeInsetTop() - : 0); - } - - return 0; - } - private void requestUpdate() { if (mViewUpdatedRequested || mIsExpansionAnimating) { return; @@ -2484,7 +2501,7 @@ public class BubbleStackView extends FrameLayout PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer).cancel(); mAnimatingOutSurfaceContainer.setScaleX(1f); mAnimatingOutSurfaceContainer.setScaleY(1f); - mAnimatingOutSurfaceContainer.setTranslationX(0); + mAnimatingOutSurfaceContainer.setTranslationX(mExpandedViewContainer.getPaddingLeft()); mAnimatingOutSurfaceContainer.setTranslationY(0); final int[] activityViewLocation = @@ -2542,9 +2559,22 @@ public class BubbleStackView extends FrameLayout Log.d(TAG, "updateExpandedView: mIsExpanded=" + mIsExpanded); } + // Need to update the padding around the view for any insets + Insets insets = mPositioner.getInsets(); + int leftPadding = insets.left + mExpandedViewPadding; + int rightPadding = insets.right + mExpandedViewPadding; + if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) { + if (!mStackAnimationController.isStackOnLeftSide()) { + rightPadding += mPointerHeight + mBubbleSize; + } else { + leftPadding += mPointerHeight + mBubbleSize; + } + } + mExpandedViewContainer.setPadding(leftPadding, 0, rightPadding, 0); mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE); if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) { mExpandedViewContainer.setTranslationY(getExpandedViewY()); + mExpandedViewContainer.setTranslationX(0f); mExpandedBubble.getExpandedView().updateView( mExpandedViewContainer.getLocationOnScreen()); } @@ -2587,12 +2617,27 @@ public class BubbleStackView extends FrameLayout if (index == -1) { return; } - float bubbleLeftFromScreenLeft = mExpandedAnimationController.getBubbleLeft(index); - float halfBubble = mBubbleSize / 2f; - float bubbleCenter = bubbleLeftFromScreenLeft + halfBubble; - // Padding might be adjusted for insets, so get it directly from the view - bubbleCenter -= mExpandedViewContainer.getPaddingLeft(); - mExpandedBubble.getExpandedView().setPointerPosition(bubbleCenter); + float bubblePosition = mExpandedAnimationController.getBubbleXOrYForOrientation(index); + if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) { + float x = mStackOnLeftOrWillBe + ? mPositioner.getAvailableRect().left + : mPositioner.getAvailableRect().right + - mExpandedViewContainer.getPaddingRight() + - mPointerHeight; + float bubbleCenter = bubblePosition - getExpandedViewY() + (mBubbleSize / 2f); + mExpandedBubble.getExpandedView().setPointerPosition( + x, + bubbleCenter, + true, + mStackOnLeftOrWillBe); + } else { + float bubbleCenter = bubblePosition + (mBubbleSize / 2f); + mExpandedBubble.getExpandedView().setPointerPosition( + bubbleCenter, + getExpandedViewY(), + false, + mStackOnLeftOrWillBe); + } } /** @@ -2621,7 +2666,7 @@ public class BubbleStackView extends FrameLayout * @return the normalized x-axis position of the bubble stack rounded to 4 decimal places. */ public float getNormalizedXPosition() { - return new BigDecimal(getStackPosition().x / mDisplaySize.x) + return new BigDecimal(getStackPosition().x / mPositioner.getAvailableRect().width()) .setScale(4, RoundingMode.CEILING.HALF_UP) .floatValue(); } @@ -2630,7 +2675,7 @@ public class BubbleStackView extends FrameLayout * @return the normalized y-axis position of the bubble stack rounded to 4 decimal places. */ public float getNormalizedYPosition() { - return new BigDecimal(getStackPosition().y / mDisplaySize.y) + return new BigDecimal(getStackPosition().y / mPositioner.getAvailableRect().height()) .setScale(4, RoundingMode.CEILING.HALF_UP) .floatValue(); } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java index 39c750de28acd..a84685b76c893 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java @@ -128,6 +128,9 @@ public interface Bubbles { /** Set a listener to be notified of when overflow view update. */ void setOverflowListener(BubbleData.Listener listener); - /** The task listener for events in bubble tasks. **/ + /** The task listener for events in bubble tasks. */ MultiWindowTaskListener getTaskManager(); + + /** Contains information to help position things on the screen. */ + BubblePositioner getPositioner(); } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java index 7fdc01961aa56..5a70401abb4ad 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java @@ -19,11 +19,9 @@ package com.android.systemui.bubbles.animation; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Path; -import android.graphics.Point; import android.graphics.PointF; -import android.view.DisplayCutout; +import android.graphics.Rect; import android.view.View; -import android.view.WindowInsets; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -32,6 +30,7 @@ import androidx.dynamicanimation.animation.SpringForce; import com.android.systemui.Interpolators; import com.android.systemui.R; +import com.android.systemui.bubbles.BubblePositioner; import com.android.wm.shell.animation.PhysicsAnimator; import com.android.wm.shell.common.magnetictarget.MagnetizedObject; @@ -83,16 +82,8 @@ public class ExpandedAnimationController private float mBubblePaddingTop; /** Size of each bubble. */ private float mBubbleSizePx; - /** Space between bubbles in row above expanded view. */ - private float mSpaceBetweenBubbles; - /** Height of the status bar. */ - private float mStatusBarHeight; - /** Size of display. */ - private Point mDisplaySize; /** Max number of bubbles shown in row above expanded view. */ private int mBubblesMaxRendered; - /** What the current screen orientation is. */ - private int mScreenOrientation; private boolean mAnimatingExpand = false; @@ -104,7 +95,8 @@ public class ExpandedAnimationController private boolean mPreparingToCollapse = false; private boolean mAnimatingCollapse = false; - private @Nullable Runnable mAfterExpand; + @Nullable + private Runnable mAfterExpand; private Runnable mAfterCollapse; private PointF mCollapsePoint; @@ -138,9 +130,12 @@ public class ExpandedAnimationController */ private Runnable mOnBubbleAnimatedOutAction; - public ExpandedAnimationController(Point displaySize, int expandedViewPadding, - int orientation, Runnable onBubbleAnimatedOutAction) { - updateResources(orientation, displaySize); + private BubblePositioner mPositioner; + + public ExpandedAnimationController(BubblePositioner positioner, int expandedViewPadding, + Runnable onBubbleAnimatedOutAction) { + mPositioner = positioner; + updateResources(); mExpandedViewPadding = expandedViewPadding; mOnBubbleAnimatedOutAction = onBubbleAnimatedOutAction; } @@ -152,7 +147,8 @@ public class ExpandedAnimationController private boolean mBubbleDraggedOutEnough = false; /** End action to run when the lead bubble's expansion animation completes. */ - @Nullable private Runnable mLeadBubbleEndAction; + @Nullable + private Runnable mLeadBubbleEndAction; /** * Animates expanding the bubbles into a row along the top of the screen, optionally running an @@ -200,28 +196,17 @@ public class ExpandedAnimationController /** * Update effective screen width based on current orientation. - * @param orientation Landscape or portrait. - * @param displaySize Updated display size. */ - public void updateResources(int orientation, Point displaySize) { - mScreenOrientation = orientation; - mDisplaySize = displaySize; + public void updateResources() { if (mLayout == null) { return; } Resources res = mLayout.getContext().getResources(); mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top); - mStatusBarHeight = res.getDimensionPixelSize( - com.android.internal.R.dimen.status_bar_height); mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset); mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top); mBubbleSizePx = res.getDimensionPixelSize(R.dimen.individual_bubble_size); mBubblesMaxRendered = res.getInteger(R.integer.bubbles_max_rendered); - - // Includes overflow button. - float totalGapWidth = getWidthForDisplayingBubbles() - (mExpandedViewPadding * 2) - - (mBubblesMaxRendered + 1) * mBubbleSizePx; - mSpaceBetweenBubbles = totalGapWidth / mBubblesMaxRendered; } /** @@ -270,9 +255,18 @@ public class ExpandedAnimationController // If we're expanding, first draw a line from the bubble's current position to the // top of the screen. path.lineTo(bubble.getTranslationX(), expandedY); - // Then, draw a line across the screen to the bubble's resting position. - path.lineTo(getBubbleLeft(index), expandedY); + if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) { + Rect availableRect = mPositioner.getAvailableRect(); + boolean onLeft = mCollapsePoint != null + && mCollapsePoint.x < (availableRect.width() / 2f); + float translationX = onLeft + ? availableRect.left + mExpandedViewPadding + : availableRect.right - mBubbleSizePx - mExpandedViewPadding; + path.lineTo(translationX, getBubbleXOrYForOrientation(index)); + } else { + path.lineTo(getBubbleXOrYForOrientation(index), expandedY); + } } else { final float stackedX = mCollapsePoint.x; @@ -411,7 +405,8 @@ public class ExpandedAnimationController updateBubblePositions(); } - @Nullable public View getDraggedOutBubble() { + @Nullable + public View getDraggedOutBubble() { return mMagnetizedBubbleDraggingOut == null ? null : mMagnetizedBubbleDraggingOut.getUnderlyingObject(); @@ -430,7 +425,7 @@ public class ExpandedAnimationController final int index = mLayout.indexOfChild(bubbleView); animationForChildAtIndex(index) - .position(getBubbleLeft(index), getExpandedY()) + .position(getBubbleXOrYForOrientation(index), getExpandedY()) .withPositionStartVelocities(velX, velY) .start(() -> bubbleView.setTranslationZ(0f) /* after */); @@ -457,15 +452,7 @@ public class ExpandedAnimationController /** The Y value of the row of expanded bubbles. */ public float getExpandedY() { - if (mLayout == null || mLayout.getRootWindowInsets() == null) { - return 0; - } - final WindowInsets insets = mLayout.getRootWindowInsets(); - return mBubblePaddingTop + Math.max( - mStatusBarHeight, - insets.getDisplayCutout() != null - ? insets.getDisplayCutout().getSafeInsetTop() - : 0); + return mPositioner.getAvailableRect().top + mBubblePaddingTop; } /** Description of current animation controller state. */ @@ -479,7 +466,7 @@ public class ExpandedAnimationController @Override void onActiveControllerForLayout(PhysicsAnimationLayout layout) { - updateResources(mScreenOrientation, mDisplaySize); + updateResources(); // Ensure that all child views are at 1x scale, and visible, in case they were animating // in. @@ -524,7 +511,7 @@ public class ExpandedAnimationController } else if (mAnimatingCollapse) { startOrUpdatePathAnimation(false /* expanding */); } else { - child.setTranslationX(getBubbleLeft(index)); + child.setTranslationX(getBubbleXOrYForOrientation(index)); // If we're preparing to collapse, don't start animations since the collapse animation // will take over and animate the new bubble into the correct (stacked) position. @@ -593,76 +580,56 @@ public class ExpandedAnimationController return; } - animationForChild(bubble) - .translationX(getBubbleLeft(i)) - .start(); - } - } - - /** - * @param index Bubble index in row. - * @return Bubble left x from left edge of screen. - */ - public float getBubbleLeft(int index) { - final float bubbleFromRowLeft = index * (mBubbleSizePx + mSpaceBetweenBubbles); - return getRowLeft() + bubbleFromRowLeft; - } - - /** - * When expanded, the bubbles are centered in the screen. In portrait, all available space is - * used. In landscape we have too much space so the value is restricted. This method accounts - * for window decorations (nav bar, cutouts). - * - * @return the desired width to display the expanded bubbles in. - */ - public float getWidthForDisplayingBubbles() { - final float availableWidth = getAvailableScreenWidth(true /* includeStableInsets */); - if (mScreenOrientation == Configuration.ORIENTATION_LANDSCAPE) { - // display size y in landscape will be the smaller dimension of the screen - return Math.max(mDisplaySize.y, availableWidth * CENTER_BUBBLES_LANDSCAPE_PERCENT); - } else { - return availableWidth; - } - } - - /** - * Determines the available screen width without the cutout. - * - * @param subtractStableInsets Whether or not stable insets should also be removed from the - * returned width. - * @return the total screen width available accounting for cutouts and insets, - * iff {@param includeStableInsets} is true. - */ - private float getAvailableScreenWidth(boolean subtractStableInsets) { - float availableSize = mDisplaySize.x; - WindowInsets insets = mLayout != null ? mLayout.getRootWindowInsets() : null; - if (insets != null) { - int cutoutLeft = 0; - int cutoutRight = 0; - DisplayCutout cutout = insets.getDisplayCutout(); - if (cutout != null) { - cutoutLeft = cutout.getSafeInsetLeft(); - cutoutRight = cutout.getSafeInsetRight(); + if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) { + Rect availableRect = mPositioner.getAvailableRect(); + boolean onLeft = mCollapsePoint != null + && mCollapsePoint.x < (availableRect.width() / 2f); + animationForChild(bubble) + .translationX(onLeft + ? availableRect.left + mExpandedViewPadding + : availableRect.right - mBubbleSizePx - mExpandedViewPadding) + .translationY(getBubbleXOrYForOrientation(i)) + .start(); + } else { + animationForChild(bubble) + .translationX(getBubbleXOrYForOrientation(i)) + .translationY(getExpandedY()) + .start(); } - final int stableLeft = subtractStableInsets ? insets.getStableInsetLeft() : 0; - final int stableRight = subtractStableInsets ? insets.getStableInsetRight() : 0; - availableSize -= Math.max(stableLeft, cutoutLeft); - availableSize -= Math.max(stableRight, cutoutRight); } - return availableSize; } - private float getRowLeft() { + /** + * When bubbles are expanded in portrait, they display at the top of the screen in a horizontal + * row. When in landscape, they show at the left or right side in a vertical row. This method + * accounts for screen orientation and will return an x or y value for the position of the + * bubble in the row. + * + * @param index Bubble index in row. + * @return the y position of the bubble if {@link Configuration#ORIENTATION_LANDSCAPE} and the + * x position if {@link Configuration#ORIENTATION_PORTRAIT}. + */ + public float getBubbleXOrYForOrientation(int index) { if (mLayout == null) { return 0; } - float rowWidth = (mLayout.getChildCount() * mBubbleSizePx) - + ((mLayout.getChildCount() - 1) * mSpaceBetweenBubbles); - - // This display size we're using includes the size of the insets, we want the true - // center of the display minus the notch here, which means we should include the - // stable insets (e.g. status bar, nav bar) in this calculation. - final float trueCenter = getAvailableScreenWidth(false /* subtractStableInsets */) / 2f; - return trueCenter - (rowWidth / 2f); + Rect availableRect = mPositioner.getAvailableRect(); + final boolean isLandscape = + mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE; + final float availableSpace = isLandscape + ? availableRect.height() + : availableRect.width(); + final float spaceForMaxBubbles = (mExpandedViewPadding * 2) + + (mBubblesMaxRendered + 1) * mBubbleSizePx; + final float spaceBetweenBubbles = + (availableSpace - spaceForMaxBubbles) / mBubblesMaxRendered; + final float expandedStackSize = (mLayout.getChildCount() * mBubbleSizePx) + + ((mLayout.getChildCount() - 1) * spaceBetweenBubbles); + final float centerPosition = isLandscape + ? availableRect.centerY() + : availableRect.centerX(); + final float rowStart = centerPosition - (expandedStackSize / 2f); + final float positionInBar = index * (mBubbleSizePx + spaceBetweenBubbles); + return rowStart + positionInBar; } } 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 52af856287d6a..31e1ca839e5d9 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java @@ -24,7 +24,6 @@ import android.graphics.RectF; import android.provider.Settings; import android.util.Log; import android.view.View; -import android.view.WindowInsets; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -35,6 +34,7 @@ import androidx.dynamicanimation.animation.SpringAnimation; import androidx.dynamicanimation.animation.SpringForce; import com.android.systemui.R; +import com.android.systemui.bubbles.BubblePositioner; import com.android.systemui.bubbles.BubbleStackView; import com.android.wm.shell.animation.PhysicsAnimator; import com.android.wm.shell.common.FloatingContentCoordinator; @@ -59,12 +59,6 @@ public class StackAnimationController extends private static final String TAG = "Bubbs.StackCtrl"; - /** Scale factor to use initially for new bubbles being animated in. */ - private static final float ANIMATE_IN_STARTING_SCALE = 1.15f; - - /** Translation factor (multiplied by stack offset) to use for bubbles being animated in/out. */ - private static final int ANIMATE_TRANSLATION_FACTOR = 4; - /** Values to use for animating bubbles in. */ private static final float ANIMATE_IN_STIFFNESS = 1000f; private static final int ANIMATE_IN_START_DELAY = 25; @@ -198,10 +192,8 @@ public class StackAnimationController extends private int mBubblePaddingTop; /** How far offscreen the stack rests. */ private int mBubbleOffscreen; - /** How far down the screen the stack starts, when there is no pre-existing location. */ - private int mStackStartingVerticalOffset; - /** Height of the status bar. */ - private float mStatusBarHeight; + /** Contains display size, orientation, and inset information. */ + private BubblePositioner mPositioner; /** FloatingContentCoordinator instance for resolving floating content conflicts. */ private FloatingContentCoordinator mFloatingContentCoordinator; @@ -266,10 +258,12 @@ public class StackAnimationController extends public StackAnimationController( FloatingContentCoordinator floatingContentCoordinator, IntSupplier bubbleCountSupplier, - Runnable onBubbleAnimatedOutAction) { + Runnable onBubbleAnimatedOutAction, + BubblePositioner positioner) { mFloatingContentCoordinator = floatingContentCoordinator; mBubbleCountSupplier = bubbleCountSupplier; mOnBubbleAnimatedOutAction = onBubbleAnimatedOutAction; + mPositioner = positioner; } /** @@ -583,45 +577,12 @@ public class StackAnimationController extends * be animated or dragged beyond them. */ public RectF getAllowableStackPositionRegion() { - final WindowInsets insets = mLayout.getRootWindowInsets(); - final RectF allowableRegion = new RectF(); - if (insets != null) { - allowableRegion.left = - -mBubbleOffscreen - + Math.max( - insets.getSystemWindowInsetLeft(), - insets.getDisplayCutout() != null - ? insets.getDisplayCutout().getSafeInsetLeft() - : 0); - allowableRegion.right = - mLayout.getWidth() - - mBubbleSize - + mBubbleOffscreen - - Math.max( - insets.getSystemWindowInsetRight(), - insets.getDisplayCutout() != null - ? insets.getDisplayCutout().getSafeInsetRight() - : 0); - - allowableRegion.top = - mBubblePaddingTop - + Math.max( - mStatusBarHeight, - insets.getDisplayCutout() != null - ? insets.getDisplayCutout().getSafeInsetTop() - : 0); - allowableRegion.bottom = - mLayout.getHeight() - - mBubbleSize - - mBubblePaddingTop - - (mImeHeight != UNSET ? mImeHeight + mBubblePaddingTop : 0f) - - Math.max( - insets.getStableInsetBottom(), - insets.getDisplayCutout() != null - ? insets.getDisplayCutout().getSafeInsetBottom() - : 0); - } - + final RectF allowableRegion = new RectF(mPositioner.getAvailableRect()); + allowableRegion.left -= mBubbleOffscreen; + allowableRegion.top += mBubblePaddingTop; + allowableRegion.right += mBubbleOffscreen - mBubbleSize; + allowableRegion.bottom -= mBubblePaddingTop + mBubbleSize + + (mImeHeight != UNSET ? mImeHeight + mBubblePaddingTop : 0f); return allowableRegion; } @@ -824,22 +785,15 @@ public class StackAnimationController extends mBubbleBitmapSize = res.getDimensionPixelSize(R.dimen.bubble_bitmap_size); mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top); mBubbleOffscreen = res.getDimensionPixelSize(R.dimen.bubble_stack_offscreen); - mStackStartingVerticalOffset = - res.getDimensionPixelSize(R.dimen.bubble_stack_starting_offset_y); - mStatusBarHeight = - res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); } /** - * Update effective screen width based on current orientation. - * @param orientation Landscape or portrait. + * Update resources. */ - public void updateResources(int orientation) { + public void updateResources() { if (mLayout != null) { Resources res = mLayout.getContext().getResources(); mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top); - mStatusBarHeight = res.getDimensionPixelSize( - com.android.internal.R.dimen.status_bar_height); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java index b082d17e58ca1..d9e9a8b26f0fc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java @@ -45,6 +45,9 @@ import android.app.INotificationManager; import android.app.Notification; import android.app.PendingIntent; import android.content.pm.LauncherApps; +import android.content.res.Configuration; +import android.graphics.Insets; +import android.graphics.Rect; import android.hardware.display.AmbientDisplayConfiguration; import android.hardware.face.FaceManager; import android.os.Handler; @@ -80,10 +83,8 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationTestHelper; -import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.phone.LockscreenLockIconController; import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl; import com.android.systemui.statusbar.phone.NotificationShadeWindowView; import com.android.systemui.statusbar.phone.ShadeController; @@ -178,8 +179,6 @@ public class BubbleControllerTest extends SysuiTestCase { @Mock private ShadeController mShadeController; @Mock - private NotificationShelfComponent mNotificationShelfComponent; - @Mock private NotifPipeline mNotifPipeline; @Mock private FeatureFlags mFeatureFlagsOldPipeline; @@ -191,11 +190,12 @@ public class BubbleControllerTest extends SysuiTestCase { private IStatusBarService mStatusBarService; @Mock private LauncherApps mLauncherApps; - @Mock private LockscreenLockIconController mLockIconController; - - @Mock private WindowManagerShellWrapper mWindowManagerShellWrapper; - - @Mock private BubbleLogger mBubbleLogger; + @Mock + private WindowManagerShellWrapper mWindowManagerShellWrapper; + @Mock + private BubbleLogger mBubbleLogger; + @Mock + private BubblePositioner mPositioner; private BubbleData mBubbleData; @@ -210,7 +210,6 @@ public class BubbleControllerTest extends SysuiTestCase { mContext.addMockSystemService(FaceManager.class, mFaceManager); when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors); - // Bubbles get added to status bar window view mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext, mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController, mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController, @@ -241,6 +240,13 @@ public class BubbleControllerTest extends SysuiTestCase { mSysUiStateBubblesExpanded = (sysUiFlags & QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED) != 0); + mBubbleData = new BubbleData(mContext, mBubbleLogger); + + Rect availableRect = new Rect(0, 0, 1000, 5000); + when(mPositioner.getAvailableRect()).thenReturn(availableRect); + when(mPositioner.getOrientation()).thenReturn(Configuration.ORIENTATION_PORTRAIT); + when(mPositioner.getInsets()).thenReturn(Insets.of(0, 0, 0, 0)); + TestableNotificationInterruptStateProviderImpl interruptionStateProvider = new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(), mock(PowerManager.class), @@ -252,7 +258,6 @@ public class BubbleControllerTest extends SysuiTestCase { mock(HeadsUpManager.class), mock(Handler.class) ); - mBubbleData = new BubbleData(mContext, mBubbleLogger); when(mFeatureFlagsOldPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(false); mBubbleController = new TestableBubbleController( mContext, @@ -279,7 +284,8 @@ public class BubbleControllerTest extends SysuiTestCase { mLauncherApps, mBubbleLogger, mock(Handler.class), - mock(ShellTaskOrganizer.class)); + mock(ShellTaskOrganizer.class), + mPositioner); mBubbleController.setExpandListener(mBubbleExpandListener); // Get a reference to the BubbleController's entry listener diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java index cbacd5393ccfe..b9394ff3f26a6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java @@ -42,7 +42,9 @@ import android.app.INotificationManager; import android.app.Notification; import android.app.PendingIntent; import android.content.pm.LauncherApps; -import android.content.res.Resources; +import android.content.res.Configuration; +import android.graphics.Insets; +import android.graphics.Rect; import android.hardware.display.AmbientDisplayConfiguration; import android.hardware.face.FaceManager; import android.os.Handler; @@ -170,8 +172,6 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Mock ColorExtractor.GradientColors mGradientColors; @Mock - private Resources mResources; - @Mock private ShadeController mShadeController; @Mock private NotificationShelfComponent mNotificationShelfComponent; @@ -191,6 +191,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { private WindowManagerShellWrapper mWindowManagerShellWrapper; @Mock private BubbleLogger mBubbleLogger; + @Mock + private BubblePositioner mPositioner; private BubbleData mBubbleData; @@ -223,7 +225,6 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { }, mLockIconController); - // Bubbles get added to status bar window view mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext, mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController, mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController, @@ -243,6 +244,13 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { mZenModeConfig.suppressedVisualEffects = 0; when(mZenModeController.getConfig()).thenReturn(mZenModeConfig); + mBubbleData = new BubbleData(mContext, mBubbleLogger); + + Rect availableRect = new Rect(0, 0, 1000, 5000); + when(mPositioner.getAvailableRect()).thenReturn(availableRect); + when(mPositioner.getOrientation()).thenReturn(Configuration.ORIENTATION_PORTRAIT); + when(mPositioner.getInsets()).thenReturn(Insets.of(0, 0, 0, 0)); + TestableNotificationInterruptStateProviderImpl interruptionStateProvider = new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(), mock(PowerManager.class), @@ -254,7 +262,6 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { mock(HeadsUpManager.class), mock(Handler.class) ); - mBubbleData = new BubbleData(mContext, mBubbleLogger); when(mFeatureFlagsNewPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(true); mBubbleController = new TestableBubbleController( mContext, @@ -281,7 +288,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { mLauncherApps, mBubbleLogger, mock(Handler.class), - mock(ShellTaskOrganizer.class)); + mock(ShellTaskOrganizer.class), + mPositioner); mBubbleController.addNotifCallback(mNotifCallback); mBubbleController.setExpandListener(mBubbleExpandListener); diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java index 27c6fc1477726..aaeee16dc1fd0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java @@ -70,7 +70,8 @@ public class TestableBubbleController extends BubbleController { LauncherApps launcherApps, BubbleLogger bubbleLogger, Handler mainHandler, - ShellTaskOrganizer shellTaskOrganizer) { + ShellTaskOrganizer shellTaskOrganizer, + BubblePositioner positioner) { super(context, notificationShadeWindowController, statusBarStateController, shadeController, data, Runnable::run, configurationController, interruptionStateProvider, @@ -78,7 +79,7 @@ public class TestableBubbleController extends BubbleController { notifPipeline, featureFlags, dumpManager, floatingContentCoordinator, dataRepository, sysUiState, notificationManager, statusBarService, windowManager, windowManagerShellWrapper, launcherApps, bubbleLogger, - mainHandler, shellTaskOrganizer); + mainHandler, shellTaskOrganizer, positioner); setInflateSynchronously(true); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java index 6a1486382eac4..a5bb8ea235593 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java @@ -17,26 +17,30 @@ package com.android.systemui.bubbles.animation; import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import android.annotation.SuppressLint; import android.content.res.Configuration; import android.content.res.Resources; -import android.graphics.Point; +import android.graphics.Insets; import android.graphics.PointF; +import android.graphics.Rect; import android.testing.AndroidTestingRunner; import android.view.View; +import android.view.WindowManager; import android.widget.FrameLayout; import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.test.filters.SmallTest; import com.android.systemui.R; +import com.android.systemui.bubbles.BubblePositioner; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mockito; import org.mockito.Spy; @SmallTest @@ -46,47 +50,45 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC private int mDisplayWidth = 500; private int mDisplayHeight = 1000; private int mExpandedViewPadding = 10; - private int mOrientation = Configuration.ORIENTATION_PORTRAIT; - private float mLauncherGridDiff = 30f; - - private Runnable mOnBubbleAnimatedOutAction = Mockito.mock(Runnable.class); + private Runnable mOnBubbleAnimatedOutAction = mock(Runnable.class); @Spy - private ExpandedAnimationController mExpandedController = - new ExpandedAnimationController( - new Point(mDisplayWidth, mDisplayHeight) /* displaySize */, - mExpandedViewPadding, mOrientation, mOnBubbleAnimatedOutAction); + ExpandedAnimationController mExpandedController; private int mStackOffset; - private float mBubblePaddingTop; - private float mBubbleSize; - private PointF mExpansionPoint; + @SuppressLint("VisibleForTests") @Before public void setUp() throws Exception { super.setUp(); + + BubblePositioner positioner = new BubblePositioner(getContext(), mock(WindowManager.class)); + positioner.update(Configuration.ORIENTATION_PORTRAIT, + Insets.of(0, 0, 0, 0), + new Rect(0, 0, mDisplayWidth, mDisplayHeight)); + mExpandedController = new ExpandedAnimationController(positioner, mExpandedViewPadding, + mOnBubbleAnimatedOutAction); + addOneMoreThanBubbleLimitBubbles(); mLayout.setActiveController(mExpandedController); Resources res = mLayout.getResources(); mStackOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_offset); - mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top); - mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size); mExpansionPoint = new PointF(100, 100); } @Test @Ignore public void testExpansionAndCollapse() throws InterruptedException { - Runnable afterExpand = Mockito.mock(Runnable.class); + Runnable afterExpand = mock(Runnable.class); mExpandedController.expandFromStack(afterExpand); waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); testBubblesInCorrectExpandedPositions(); verify(afterExpand).run(); - Runnable afterCollapse = Mockito.mock(Runnable.class); + Runnable afterCollapse = mock(Runnable.class); mExpandedController.collapseBackToStack(mExpansionPoint, afterCollapse); waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); @@ -121,7 +123,7 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC /** Expand the stack and wait for animations to finish. */ private void expand() throws InterruptedException { - mExpandedController.expandFromStack(Mockito.mock(Runnable.class)); + mExpandedController.expandFromStack(mock(Runnable.class)); waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); } @@ -141,51 +143,12 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC private void testBubblesInCorrectExpandedPositions() { // Check all the visible bubbles to see if they're in the right place. for (int i = 0; i < mLayout.getChildCount(); i++) { - assertEquals(getBubbleLeft(i), + float expectedPosition = mExpandedController.getBubbleXOrYForOrientation(i); + assertEquals(expectedPosition, mLayout.getChildAt(i).getTranslationX(), 2f); - assertEquals(mExpandedController.getExpandedY(), + assertEquals(expectedPosition, mLayout.getChildAt(i).getTranslationY(), 2f); } } - - /** - * @param index Bubble index in row. - * @return Bubble left x from left edge of screen. - */ - public float getBubbleLeft(int index) { - final float bubbleLeft = index * (mBubbleSize + getSpaceBetweenBubbles()); - return getRowLeft() + bubbleLeft; - } - - private float getRowLeft() { - if (mLayout == null) { - return 0; - } - int bubbleCount = mLayout.getChildCount(); - final float totalBubbleWidth = bubbleCount * mBubbleSize; - final float totalGapWidth = (bubbleCount - 1) * getSpaceBetweenBubbles(); - final float rowWidth = totalGapWidth + totalBubbleWidth; - - final float centerScreen = mDisplayWidth / 2f; - final float halfRow = rowWidth / 2f; - final float rowLeft = centerScreen - halfRow; - - return rowLeft; - } - - /** - * @return Space between bubbles in row above expanded view. - */ - private float getSpaceBetweenBubbles() { - final float rowMargins = (mExpandedViewPadding + mLauncherGridDiff) * 2; - final float maxRowWidth = mDisplayWidth - rowMargins; - - final float totalBubbleWidth = mMaxBubbles * mBubbleSize; - final float totalGapWidth = maxRowWidth - totalBubbleWidth; - - final int gapCount = mMaxBubbles - 1; - final float gapWidth = totalGapWidth / gapCount; - return gapWidth; - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java index 9242ce940bcd3..7d0abec79de1c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java @@ -18,6 +18,7 @@ package com.android.systemui.bubbles.animation; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; @@ -33,6 +34,7 @@ import androidx.dynamicanimation.animation.SpringForce; import androidx.test.filters.SmallTest; import com.android.systemui.R; +import com.android.systemui.bubbles.BubblePositioner; import com.android.wm.shell.common.FloatingContentCoordinator; import org.junit.Before; @@ -40,7 +42,6 @@ import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.Mockito; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -67,7 +68,7 @@ public class StackAnimationControllerTest extends PhysicsAnimationLayoutTestCase public int getAsInt() { return mLayout.getChildCount(); } - }, Mockito.mock(Runnable.class))); + }, mock(Runnable.class))); mLayout.setActiveController(mStackController); addOneMoreThanBubbleLimitBubbles(); mStackOffset = mLayout.getResources().getDimensionPixelSize(R.dimen.bubble_stack_offset); @@ -306,7 +307,10 @@ public class StackAnimationControllerTest extends PhysicsAnimationLayoutTestCase FloatingContentCoordinator floatingContentCoordinator, IntSupplier bubbleCountSupplier, Runnable onBubbleAnimatedOutAction) { - super(floatingContentCoordinator, bubbleCountSupplier, onBubbleAnimatedOutAction); + super(floatingContentCoordinator, + bubbleCountSupplier, + onBubbleAnimatedOutAction, + mock(BubblePositioner.class)); } @Override