Merge "Vertical bubbles in landscape"

This commit is contained in:
TreeHugger Robot
2020-10-19 18:56:02 +00:00
committed by Android (Google) Code Review
12 changed files with 524 additions and 446 deletions

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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<Boolean> onImeVisibilityChanged,
Runnable hideCurrentInputMethodCallback,
Consumer<Boolean> onBubbleExpandChanged) {
Consumer<Boolean> 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();
}

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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