Vertical bubbles in landscape
- in landscape the bubbles now show on left/right side & layout vertically
instead of remaining at the top and being horizontal
- introduces BubblePositioner to track the config changes and available
screen rect for bubbles based on insets / cutouts
- pointer can now go left or right
- fixes issue where overflow could be past screen height in landscape
Test: manual - bubbles at the top in portrait, bubbles on left / right side
in landscape, bubbles at the top with notch, bubbles on left
/ right side in landscape with notch, with/without gesture nav
Bug: 167413172
Change-Id: I4528035fd03d17d11865ddcdddc4732f5a8e616a
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2316,19 +2346,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;
|
||||
@@ -2477,7 +2494,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 =
|
||||
@@ -2535,9 +2552,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());
|
||||
}
|
||||
@@ -2580,12 +2610,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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2614,7 +2659,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();
|
||||
}
|
||||
@@ -2623,7 +2668,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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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 sideMultiplier =
|
||||
mLayout.isFirstChildXLeftOfCenter(mCollapsePoint.x) ? -1 : 1;
|
||||
@@ -413,7 +407,8 @@ public class ExpandedAnimationController
|
||||
updateBubblePositions();
|
||||
}
|
||||
|
||||
@Nullable public View getDraggedOutBubble() {
|
||||
@Nullable
|
||||
public View getDraggedOutBubble() {
|
||||
return mMagnetizedBubbleDraggingOut == null
|
||||
? null
|
||||
: mMagnetizedBubbleDraggingOut.getUnderlyingObject();
|
||||
@@ -432,7 +427,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 */);
|
||||
|
||||
@@ -459,15 +454,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. */
|
||||
@@ -481,7 +468,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.
|
||||
@@ -526,7 +513,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.
|
||||
@@ -595,76 +582,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -826,22 +787,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user