Flexible bubble sizing & fixed position
This will enable some stuff for taskbar & hopefully make customizations by ARC++ easier?? - BubblePositioner now tracks the size of the bubble & bitmap within it. Have to use a multiplier to derive the size of a couple of things now (e.g. bitmap size) - BubblePositioner now tracks the position of the bubble stack & allows a way to override / set a fixed position. If something is in fixed location, it is not draggable (however when expanded you can still drag individual bubbles to dismiss) - BubblePositioner tracks some taskbar related info - Rather than checking for "isLandscape" we want "showBubblesVertically" (e.g. device might not be landscape but should be vertical if taskbar) - Fixes an animation issue where bubbles super-overshoot the y position when expanding vertically. Bug: 167413172 Test: a lot of manual testing Test: atest WMShellTest SystemUITests Change-Id: I79948a4bd6b58fb3aa2f52e2553f3dfc31630cf1
This commit is contained in:
@@ -16,14 +16,16 @@
|
||||
-->
|
||||
<com.android.wm.shell.bubbles.BubbleMenuView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:background="#66000000"
|
||||
android:visibility="gone"
|
||||
android:id="@+id/bubble_menu_container">
|
||||
android:id="@+id/bubble_menu_container"
|
||||
tools:ignore="MissingClass">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_height="@dimen/individual_bubble_size"
|
||||
android:layout_height="@dimen/bubble_menu_item_height"
|
||||
android:layout_width="wrap_content"
|
||||
android:background="#FFFFFF"
|
||||
android:id="@+id/bubble_menu_view">
|
||||
|
||||
@@ -17,6 +17,6 @@
|
||||
<com.android.wm.shell.bubbles.BadgedImageView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/bubble_overflow_button"
|
||||
android:layout_width="@dimen/individual_bubble_size"
|
||||
android:layout_height="@dimen/individual_bubble_size"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/bubble_ic_overflow_button"/>
|
||||
|
||||
@@ -17,5 +17,5 @@
|
||||
<com.android.wm.shell.bubbles.BadgedImageView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/bubble_view"
|
||||
android:layout_width="@dimen/individual_bubble_size"
|
||||
android:layout_height="@dimen/individual_bubble_size"/>
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
@@ -99,12 +99,12 @@
|
||||
<dimen name="bubble_flyout_avatar_message_space">6dp</dimen>
|
||||
<!-- Padding between status bar and bubbles when displayed in expanded state -->
|
||||
<dimen name="bubble_padding_top">16dp</dimen>
|
||||
<!-- Max amount of space between bubbles when expanded. -->
|
||||
<dimen name="bubble_max_spacing">16dp</dimen>
|
||||
<!-- Size of individual bubbles. -->
|
||||
<dimen name="individual_bubble_size">60dp</dimen>
|
||||
<!-- Size of bubble bitmap. -->
|
||||
<dimen name="bubble_bitmap_size">52dp</dimen>
|
||||
<!-- Size of bubble icon bitmap. -->
|
||||
<dimen name="bubble_overflow_icon_bitmap_size">24dp</dimen>
|
||||
<!-- Extra padding added to the touchable rect for bubbles so they are easier to grab. -->
|
||||
<dimen name="bubble_touch_padding">12dp</dimen>
|
||||
<!-- Size of the circle around the bubbles when they're in the dismiss target. -->
|
||||
@@ -136,6 +136,8 @@
|
||||
<dimen name="bubble_dismiss_slop">16dp</dimen>
|
||||
<!-- Height of button allowing users to adjust settings for bubbles. -->
|
||||
<dimen name="bubble_manage_button_height">48dp</dimen>
|
||||
<!-- Height of an item in the bubble manage menu. -->
|
||||
<dimen name="bubble_menu_item_height">60dp</dimen>
|
||||
<!-- Max width of the message bubble-->
|
||||
<dimen name="bubble_message_max_width">144dp</dimen>
|
||||
<!-- Min width of the message bubble -->
|
||||
|
||||
@@ -31,7 +31,6 @@ import android.util.PathParser;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.android.launcher3.icons.DotRenderer;
|
||||
import com.android.wm.shell.R;
|
||||
import com.android.wm.shell.animation.Interpolators;
|
||||
|
||||
import java.util.EnumSet;
|
||||
@@ -75,9 +74,8 @@ public class BadgedImageView extends ImageView {
|
||||
private boolean mDotIsAnimating = false;
|
||||
|
||||
private BubbleViewProvider mBubble;
|
||||
private BubblePositioner mPositioner;
|
||||
|
||||
private int mBubbleBitmapSize;
|
||||
private int mBubbleSize;
|
||||
private DotRenderer mDotRenderer;
|
||||
private DotRenderer.DrawParams mDrawParams;
|
||||
private boolean mOnLeft;
|
||||
@@ -101,18 +99,21 @@ public class BadgedImageView extends ImageView {
|
||||
public BadgedImageView(Context context, AttributeSet attrs, int defStyleAttr,
|
||||
int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
mBubbleBitmapSize = getResources().getDimensionPixelSize(R.dimen.bubble_bitmap_size);
|
||||
mBubbleSize = getResources().getDimensionPixelSize(R.dimen.individual_bubble_size);
|
||||
mDrawParams = new DotRenderer.DrawParams();
|
||||
|
||||
Path iconPath = PathParser.createPathFromPathData(
|
||||
getResources().getString(com.android.internal.R.string.config_icon_mask));
|
||||
mDotRenderer = new DotRenderer(mBubbleBitmapSize, iconPath, DEFAULT_PATH_SIZE);
|
||||
|
||||
setFocusable(true);
|
||||
setClickable(true);
|
||||
}
|
||||
|
||||
public void initialize(BubblePositioner positioner) {
|
||||
mPositioner = positioner;
|
||||
|
||||
Path iconPath = PathParser.createPathFromPathData(
|
||||
getResources().getString(com.android.internal.R.string.config_icon_mask));
|
||||
mDotRenderer = new DotRenderer(mPositioner.getBubbleBitmapSize(),
|
||||
iconPath, DEFAULT_PATH_SIZE);
|
||||
}
|
||||
|
||||
public void showDotAndBadge(boolean onLeft) {
|
||||
removeDotSuppressionFlag(BadgedImageView.SuppressionFlag.BEHIND_STACK);
|
||||
animateDotBadgePositions(onLeft);
|
||||
@@ -186,7 +187,8 @@ public class BadgedImageView extends ImageView {
|
||||
* @param iconPath The new icon path to use when calculating dot position.
|
||||
*/
|
||||
void drawDot(Path iconPath) {
|
||||
mDotRenderer = new DotRenderer(mBubbleBitmapSize, iconPath, DEFAULT_PATH_SIZE);
|
||||
mDotRenderer = new DotRenderer(mPositioner.getBubbleBitmapSize(),
|
||||
iconPath, DEFAULT_PATH_SIZE);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@@ -310,13 +312,13 @@ public class BadgedImageView extends ImageView {
|
||||
|
||||
bubbleCanvas.setDrawFilter(new PaintFlagsDrawFilter(DITHER_FLAG, FILTER_BITMAP_FLAG));
|
||||
bubbleCanvas.setBitmap(bubble);
|
||||
|
||||
final int badgeSize = (int) (ICON_BADGE_SCALE * mBubbleSize);
|
||||
final int bubbleSize = bubble.getWidth();
|
||||
final int badgeSize = (int) (ICON_BADGE_SCALE * bubbleSize);
|
||||
if (mOnLeft) {
|
||||
badge.setBounds(0, mBubbleSize - badgeSize, badgeSize, mBubbleSize);
|
||||
badge.setBounds(0, bubbleSize - badgeSize, badgeSize, bubbleSize);
|
||||
} else {
|
||||
badge.setBounds(mBubbleSize - badgeSize, mBubbleSize - badgeSize,
|
||||
mBubbleSize, mBubbleSize);
|
||||
badge.setBounds(bubbleSize - badgeSize, bubbleSize - badgeSize,
|
||||
bubbleSize, bubbleSize);
|
||||
}
|
||||
badge.draw(bubbleCanvas);
|
||||
bubbleCanvas.setBitmap(null);
|
||||
|
||||
@@ -310,7 +310,7 @@ public class Bubble implements BubbleViewProvider {
|
||||
*
|
||||
* @param callback the callback to notify one the bubble is ready to be displayed.
|
||||
* @param context the context for the bubble.
|
||||
* @param controller
|
||||
* @param controller the bubble controller.
|
||||
* @param stackView the stackView the bubble is eventually added to.
|
||||
* @param iconFactory the iconfactory use to create badged images for the bubble.
|
||||
*/
|
||||
|
||||
@@ -95,12 +95,6 @@ public class BubbleController implements Bubbles {
|
||||
private BubblePositioner mBubblePositioner;
|
||||
private SysuiProxy mSysuiProxy;
|
||||
|
||||
/**
|
||||
* The relative position of the stack when we removed it and nulled it out. If the stack is
|
||||
* re-created, it will re-appear at this position.
|
||||
*/
|
||||
@Nullable private BubbleStackView.RelativeStackPosition mPositionFromRemovedStack;
|
||||
|
||||
// Tracks the id of the current (foreground) user.
|
||||
private int mCurrentUserId;
|
||||
// Saves notification keys of active bubbles when users are switched.
|
||||
@@ -176,12 +170,12 @@ public class BubbleController implements Bubbles {
|
||||
Handler mainHandler,
|
||||
ShellTaskOrganizer organizer) {
|
||||
BubbleLogger logger = new BubbleLogger(uiEventLogger);
|
||||
return new BubbleController(context,
|
||||
new BubbleData(context, logger), synchronizer,
|
||||
floatingContentCoordinator, new BubbleDataRepository(context, launcherApps),
|
||||
statusBarService, windowManager,
|
||||
windowManagerShellWrapper, launcherApps, logger, mainHandler, organizer,
|
||||
new BubblePositioner(context, windowManager));
|
||||
BubblePositioner positioner = new BubblePositioner(context, windowManager);
|
||||
BubbleData data = new BubbleData(context, logger);
|
||||
return new BubbleController(context, data, synchronizer, floatingContentCoordinator,
|
||||
new BubbleDataRepository(context, launcherApps),
|
||||
statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
|
||||
logger, mainHandler, organizer, positioner);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,6 +201,7 @@ public class BubbleController implements Bubbles {
|
||||
mLogger = bubbleLogger;
|
||||
mMainHandler = mainHandler;
|
||||
|
||||
mBubblePositioner = positioner;
|
||||
mBubbleData = data;
|
||||
mBubbleData.setListener(mBubbleDataListener);
|
||||
mBubbleData.setSuppressionChangedListener(bubble -> {
|
||||
@@ -249,7 +244,6 @@ public class BubbleController implements Bubbles {
|
||||
|
||||
mBubbleIconFactory = new BubbleIconFactory(context);
|
||||
mTaskOrganizer = organizer;
|
||||
mBubblePositioner = positioner;
|
||||
|
||||
launcherApps.registerCallback(new LauncherApps.Callback() {
|
||||
@Override
|
||||
@@ -388,7 +382,6 @@ public class BubbleController implements Bubbles {
|
||||
if (mStackView == null) {
|
||||
mStackView = new BubbleStackView(
|
||||
mContext, this, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator);
|
||||
mStackView.setStackStartPosition(mPositionFromRemovedStack);
|
||||
mStackView.addView(mBubbleScrim);
|
||||
mStackView.onOrientationChanged();
|
||||
if (mExpandListener != null) {
|
||||
@@ -430,6 +423,8 @@ public class BubbleController implements Bubbles {
|
||||
try {
|
||||
mAddedToWindowManager = true;
|
||||
mWindowManager.addView(mStackView, mWmLayoutParams);
|
||||
// Position info is dependent on us being attached to a window
|
||||
mBubblePositioner.update(mOrientation);
|
||||
} catch (IllegalStateException e) {
|
||||
// This means the stack has already been added. This shouldn't happen...
|
||||
e.printStackTrace();
|
||||
@@ -449,7 +444,6 @@ public class BubbleController implements Bubbles {
|
||||
try {
|
||||
mAddedToWindowManager = false;
|
||||
if (mStackView != null) {
|
||||
mPositionFromRemovedStack = mStackView.getRelativeStackPosition();
|
||||
mWindowManager.removeView(mStackView);
|
||||
mStackView.removeView(mBubbleScrim);
|
||||
mStackView = null;
|
||||
@@ -544,7 +538,7 @@ public class BubbleController implements Bubbles {
|
||||
}
|
||||
if (newConfig.fontScale != mFontScale) {
|
||||
mFontScale = newConfig.fontScale;
|
||||
mStackView.updateFlyout(mFontScale);
|
||||
mStackView.updateFontScale(mFontScale);
|
||||
}
|
||||
if (newConfig.getLayoutDirection() != mLayoutDirection) {
|
||||
mLayoutDirection = newConfig.getLayoutDirection();
|
||||
|
||||
@@ -45,6 +45,7 @@ public class BubbleDebugConfig {
|
||||
static final boolean DEBUG_EXPERIMENTS = true;
|
||||
static final boolean DEBUG_OVERFLOW = false;
|
||||
static final boolean DEBUG_USER_EDUCATION = false;
|
||||
static final boolean DEBUG_POSITIONER = false;
|
||||
|
||||
private static final boolean FORCE_SHOW_USER_EDUCATION = false;
|
||||
private static final String FORCE_SHOW_USER_EDUCATION_SETTING =
|
||||
|
||||
@@ -639,7 +639,9 @@ public class BubbleExpandedView extends LinearLayout {
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up anything related to the task and TaskView.
|
||||
* Cleans up anything related to the task and TaskView. If this view should be reused after this
|
||||
* method is called, then {@link #initialize(BubbleController, BubbleStackView)} must be invoked
|
||||
* first.
|
||||
*/
|
||||
public void cleanUpExpandedState() {
|
||||
if (DEBUG_BUBBLE_EXPANDED_VIEW) {
|
||||
|
||||
@@ -68,9 +68,8 @@ public class BubbleFlyoutView extends FrameLayout {
|
||||
private final int mFlyoutPadding;
|
||||
private final int mFlyoutSpaceFromBubble;
|
||||
private final int mPointerSize;
|
||||
private final int mBubbleSize;
|
||||
private final int mBubbleBitmapSize;
|
||||
private final float mBubbleIconTopPadding;
|
||||
private int mBubbleSize;
|
||||
private int mBubbleBitmapSize;
|
||||
|
||||
private final int mFlyoutElevation;
|
||||
private final int mBubbleElevation;
|
||||
@@ -83,9 +82,9 @@ public class BubbleFlyoutView extends FrameLayout {
|
||||
private final TextView mMessageText;
|
||||
|
||||
/** Values related to the 'new' dot which we use to figure out where to collapse the flyout. */
|
||||
private final float mNewDotRadius;
|
||||
private final float mNewDotSize;
|
||||
private final float mOriginalDotSize;
|
||||
private float mNewDotRadius;
|
||||
private float mNewDotSize;
|
||||
private float mOriginalDotSize;
|
||||
|
||||
/**
|
||||
* The paint used to draw the background, whose color changes as the flyout transitions to the
|
||||
@@ -169,17 +168,9 @@ public class BubbleFlyoutView extends FrameLayout {
|
||||
mFlyoutSpaceFromBubble = res.getDimensionPixelSize(R.dimen.bubble_flyout_space_from_bubble);
|
||||
mPointerSize = res.getDimensionPixelSize(R.dimen.bubble_flyout_pointer_size);
|
||||
|
||||
mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
|
||||
mBubbleBitmapSize = res.getDimensionPixelSize(R.dimen.bubble_bitmap_size);
|
||||
mBubbleIconTopPadding = (mBubbleSize - mBubbleBitmapSize) / 2f;
|
||||
|
||||
mBubbleElevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
|
||||
mFlyoutElevation = res.getDimensionPixelSize(R.dimen.bubble_flyout_elevation);
|
||||
|
||||
mOriginalDotSize = SIZE_PERCENTAGE * mBubbleBitmapSize;
|
||||
mNewDotRadius = (DOT_SCALE * mOriginalDotSize) / 2f;
|
||||
mNewDotSize = mNewDotRadius * 2f;
|
||||
|
||||
final TypedArray ta = mContext.obtainStyledAttributes(
|
||||
new int[] {
|
||||
android.R.attr.colorBackgroundFloating,
|
||||
@@ -306,7 +297,15 @@ public class BubbleFlyoutView extends FrameLayout {
|
||||
@Nullable Runnable onLayoutComplete,
|
||||
@Nullable Runnable onHide,
|
||||
float[] dotCenter,
|
||||
boolean hideDot) {
|
||||
boolean hideDot,
|
||||
BubblePositioner positioner) {
|
||||
|
||||
mBubbleBitmapSize = positioner.getBubbleBitmapSize();
|
||||
mBubbleSize = positioner.getBubbleSize();
|
||||
|
||||
mOriginalDotSize = SIZE_PERCENTAGE * mBubbleBitmapSize;
|
||||
mNewDotRadius = (DOT_SCALE * mOriginalDotSize) / 2f;
|
||||
mNewDotSize = mNewDotRadius * 2f;
|
||||
|
||||
updateFlyoutMessage(flyoutMessage, parentWidth);
|
||||
|
||||
|
||||
@@ -29,14 +29,18 @@ import android.graphics.drawable.InsetDrawable
|
||||
import android.util.PathParser
|
||||
import android.util.TypedValue
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import com.android.wm.shell.R
|
||||
|
||||
/**
|
||||
* The icon in the bubble overflow is scaled down, this is the percent of the normal bubble bitmap
|
||||
* size to use.
|
||||
*/
|
||||
const val ICON_BITMAP_SIZE_PERCENT = 0.46f
|
||||
|
||||
class BubbleOverflow(
|
||||
private val context: Context,
|
||||
private val controller: BubbleController,
|
||||
private val stack: BubbleStackView
|
||||
private val positioner: BubblePositioner
|
||||
) : BubbleViewProvider {
|
||||
|
||||
private lateinit var bitmap: Bitmap
|
||||
@@ -48,41 +52,36 @@ class BubbleOverflow(
|
||||
private var showDot = false
|
||||
|
||||
private val inflater: LayoutInflater = LayoutInflater.from(context)
|
||||
private val expandedView: BubbleExpandedView = inflater
|
||||
.inflate(R.layout.bubble_expanded_view, null /* root */, false /* attachToRoot */)
|
||||
as BubbleExpandedView
|
||||
private val overflowBtn: BadgedImageView = inflater
|
||||
.inflate(R.layout.bubble_overflow_button, null /* root */, false /* attachToRoot */)
|
||||
as BadgedImageView
|
||||
private var expandedView: BubbleExpandedView?
|
||||
private var overflowBtn: BadgedImageView?
|
||||
|
||||
init {
|
||||
updateResources()
|
||||
with(expandedView) {
|
||||
initialize(controller, stack)
|
||||
setOverflow(true)
|
||||
applyThemeAttrs()
|
||||
}
|
||||
with(overflowBtn) {
|
||||
setContentDescription(context.resources.getString(
|
||||
R.string.bubble_overflow_button_content_description))
|
||||
updateBtnTheme()
|
||||
}
|
||||
bitmapSize = positioner.bubbleBitmapSize
|
||||
iconBitmapSize = (bitmapSize * ICON_BITMAP_SIZE_PERCENT).toInt()
|
||||
expandedView = null
|
||||
overflowBtn = null
|
||||
}
|
||||
|
||||
fun initialize(controller: BubbleController) {
|
||||
getExpandedView()?.initialize(controller, controller.stackView)
|
||||
getExpandedView()?.setOverflow(true)
|
||||
}
|
||||
|
||||
fun update() {
|
||||
updateResources()
|
||||
expandedView.applyThemeAttrs()
|
||||
getExpandedView()?.applyThemeAttrs()
|
||||
// Apply inset and new style to fresh icon drawable.
|
||||
overflowBtn.setImageResource(R.drawable.bubble_ic_overflow_button)
|
||||
getIconView()?.setImageResource(R.drawable.bubble_ic_overflow_button)
|
||||
updateBtnTheme()
|
||||
}
|
||||
|
||||
fun updateResources() {
|
||||
bitmapSize = context.resources.getDimensionPixelSize(R.dimen.bubble_bitmap_size)
|
||||
iconBitmapSize = context.resources.getDimensionPixelSize(
|
||||
R.dimen.bubble_overflow_icon_bitmap_size)
|
||||
val bubbleSize = context.resources.getDimensionPixelSize(R.dimen.individual_bubble_size)
|
||||
overflowBtn.setLayoutParams(FrameLayout.LayoutParams(bubbleSize, bubbleSize))
|
||||
expandedView.updateDimensions()
|
||||
bitmapSize = positioner.bubbleBitmapSize
|
||||
iconBitmapSize = (bitmapSize * 0.46f).toInt()
|
||||
val bubbleSize = positioner.bubbleSize
|
||||
overflowBtn?.setLayoutParams(FrameLayout.LayoutParams(bubbleSize, bubbleSize))
|
||||
expandedView?.updateDimensions()
|
||||
}
|
||||
|
||||
private fun updateBtnTheme() {
|
||||
@@ -92,7 +91,7 @@ class BubbleOverflow(
|
||||
val typedValue = TypedValue()
|
||||
context.theme.resolveAttribute(android.R.attr.colorAccent, typedValue, true)
|
||||
val colorAccent = res.getColor(typedValue.resourceId)
|
||||
overflowBtn.drawable?.setTint(colorAccent)
|
||||
overflowBtn?.drawable?.setTint(colorAccent)
|
||||
dotColor = colorAccent
|
||||
|
||||
val iconFactory = BubbleIconFactory(context)
|
||||
@@ -103,7 +102,7 @@ class BubbleOverflow(
|
||||
val bg = ColorDrawable(res.getColor(
|
||||
if (nightMode) R.color.bubbles_dark else R.color.bubbles_light))
|
||||
|
||||
val fg = InsetDrawable(overflowBtn.drawable,
|
||||
val fg = InsetDrawable(overflowBtn?.drawable,
|
||||
bitmapSize - iconBitmapSize /* inset */)
|
||||
bitmap = iconFactory.createBadgedIconBitmap(AdaptiveIconDrawable(bg, fg),
|
||||
null /* user */, true /* shrinkNonAdaptiveIcons */).icon
|
||||
@@ -111,7 +110,7 @@ class BubbleOverflow(
|
||||
// Update dot path
|
||||
dotPath = PathParser.createPathFromPathData(
|
||||
res.getString(com.android.internal.R.string.config_icon_mask))
|
||||
val scale = iconFactory.normalizer.getScale(overflowBtn.getDrawable(),
|
||||
val scale = iconFactory.normalizer.getScale(getIconView()!!.getDrawable(),
|
||||
null /* outBounds */, null /* path */, null /* outMaskShape */)
|
||||
val radius = BadgedImageView.DEFAULT_PATH_SIZE / 2f
|
||||
val matrix = Matrix()
|
||||
@@ -120,20 +119,31 @@ class BubbleOverflow(
|
||||
dotPath.transform(matrix)
|
||||
|
||||
// Attach BubbleOverflow to BadgedImageView
|
||||
overflowBtn.setRenderedBubble(this)
|
||||
overflowBtn.removeDotSuppressionFlag(BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE)
|
||||
overflowBtn?.setRenderedBubble(this)
|
||||
overflowBtn?.removeDotSuppressionFlag(BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE)
|
||||
}
|
||||
|
||||
fun setVisible(visible: Int) {
|
||||
overflowBtn.visibility = visible
|
||||
overflowBtn?.visibility = visible
|
||||
}
|
||||
|
||||
fun setShowDot(show: Boolean) {
|
||||
showDot = show
|
||||
overflowBtn.updateDotVisibility(true /* animate */)
|
||||
overflowBtn?.updateDotVisibility(true /* animate */)
|
||||
}
|
||||
|
||||
fun cleanUpExpandedState() {
|
||||
expandedView?.cleanUpExpandedState()
|
||||
expandedView = null
|
||||
}
|
||||
|
||||
override fun getExpandedView(): BubbleExpandedView? {
|
||||
if (expandedView == null) {
|
||||
expandedView = inflater.inflate(R.layout.bubble_expanded_view,
|
||||
null /* root */, false /* attachToRoot */) as BubbleExpandedView
|
||||
expandedView?.applyThemeAttrs()
|
||||
updateResources()
|
||||
}
|
||||
return expandedView
|
||||
}
|
||||
|
||||
@@ -158,10 +168,20 @@ class BubbleOverflow(
|
||||
}
|
||||
|
||||
override fun setContentVisibility(visible: Boolean) {
|
||||
expandedView.setContentVisibility(visible)
|
||||
expandedView?.setContentVisibility(visible)
|
||||
}
|
||||
|
||||
override fun getIconView(): View? {
|
||||
override fun getIconView(): BadgedImageView? {
|
||||
if (overflowBtn == null) {
|
||||
overflowBtn = inflater.inflate(R.layout.bubble_overflow_button,
|
||||
null /* root */, false /* attachToRoot */) as BadgedImageView
|
||||
overflowBtn?.initialize(positioner)
|
||||
overflowBtn?.setContentDescription(context.resources.getString(
|
||||
R.string.bubble_overflow_button_content_description))
|
||||
val bubbleSize = positioner.bubbleSize
|
||||
overflowBtn?.setLayoutParams(FrameLayout.LayoutParams(bubbleSize, bubbleSize))
|
||||
updateBtnTheme()
|
||||
}
|
||||
return overflowBtn
|
||||
}
|
||||
|
||||
@@ -170,7 +190,7 @@ class BubbleOverflow(
|
||||
}
|
||||
|
||||
override fun getTaskId(): Int {
|
||||
return if (expandedView != null) expandedView.getTaskId() else INVALID_TASK_ID
|
||||
return if (expandedView != null) expandedView!!.getTaskId() else INVALID_TASK_ID
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -111,11 +111,11 @@ public class BubbleOverflowActivity extends Activity {
|
||||
IBinder binder = intent.getExtras().getBinder(EXTRA_BUBBLE_CONTROLLER);
|
||||
if (binder instanceof ObjectWrapper) {
|
||||
mController = ((ObjectWrapper<BubbleController>) binder).get();
|
||||
updateOverflow();
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "Bubble overflow activity created without bubble controller!");
|
||||
}
|
||||
updateOverflow();
|
||||
}
|
||||
|
||||
void updateOverflow() {
|
||||
@@ -138,7 +138,9 @@ public class BubbleOverflowActivity extends Activity {
|
||||
final int viewHeight = recyclerViewHeight / rows;
|
||||
|
||||
mAdapter = new BubbleOverflowAdapter(getApplicationContext(), mOverflowBubbles,
|
||||
mController::promoteBubbleFromOverflow, viewWidth, viewHeight);
|
||||
mController::promoteBubbleFromOverflow,
|
||||
mController.getPositioner(),
|
||||
viewWidth, viewHeight);
|
||||
mRecyclerView.setAdapter(mAdapter);
|
||||
|
||||
mOverflowBubbles.clear();
|
||||
@@ -257,15 +259,20 @@ class BubbleOverflowAdapter extends RecyclerView.Adapter<BubbleOverflowAdapter.V
|
||||
|
||||
private Context mContext;
|
||||
private Consumer<Bubble> mPromoteBubbleFromOverflow;
|
||||
private BubblePositioner mPositioner;
|
||||
private List<Bubble> mBubbles;
|
||||
private int mWidth;
|
||||
private int mHeight;
|
||||
|
||||
public BubbleOverflowAdapter(Context context, List<Bubble> list, Consumer<Bubble> promoteBubble,
|
||||
BubbleOverflowAdapter(Context context,
|
||||
List<Bubble> list,
|
||||
Consumer<Bubble> promoteBubble,
|
||||
BubblePositioner positioner,
|
||||
int width, int height) {
|
||||
mContext = context;
|
||||
mBubbles = list;
|
||||
mPromoteBubbleFromOverflow = promoteBubble;
|
||||
mPositioner = positioner;
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
}
|
||||
@@ -295,7 +302,7 @@ class BubbleOverflowAdapter extends RecyclerView.Adapter<BubbleOverflowAdapter.V
|
||||
TextView viewName = overflowView.findViewById(R.id.bubble_view_name);
|
||||
viewName.setTextColor(textColor);
|
||||
|
||||
return new ViewHolder(overflowView);
|
||||
return new ViewHolder(overflowView, mPositioner);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -348,9 +355,10 @@ class BubbleOverflowAdapter extends RecyclerView.Adapter<BubbleOverflowAdapter.V
|
||||
public BadgedImageView iconView;
|
||||
public TextView textView;
|
||||
|
||||
public ViewHolder(LinearLayout v) {
|
||||
ViewHolder(LinearLayout v, BubblePositioner positioner) {
|
||||
super(v);
|
||||
iconView = v.findViewById(R.id.bubble_view);
|
||||
iconView.initialize(positioner);
|
||||
textView = v.findViewById(R.id.bubble_view_name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,45 +16,99 @@
|
||||
|
||||
package com.android.wm.shell.bubbles;
|
||||
|
||||
import static java.lang.annotation.RetentionPolicy.SOURCE;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowMetrics;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.wm.shell.R;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
|
||||
/**
|
||||
* 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 static final String TAG = BubbleDebugConfig.TAG_WITH_CLASS_NAME
|
||||
? "BubblePositioner"
|
||||
: BubbleDebugConfig.TAG_BUBBLES;
|
||||
|
||||
@Retention(SOURCE)
|
||||
@IntDef({TASKBAR_POSITION_RIGHT, TASKBAR_POSITION_LEFT, TASKBAR_POSITION_BOTTOM})
|
||||
@interface TaskbarPosition {}
|
||||
public static final int TASKBAR_POSITION_RIGHT = 0;
|
||||
public static final int TASKBAR_POSITION_LEFT = 1;
|
||||
public static final int TASKBAR_POSITION_BOTTOM = 2;
|
||||
|
||||
/**
|
||||
* The bitmap in the bubble is slightly smaller than the overall size of the bubble.
|
||||
* This is the percentage to scale the image down based on the overall bubble size.
|
||||
*/
|
||||
private static final float BUBBLE_BITMAP_SIZE_PERCENT = 0.86f;
|
||||
|
||||
private Context mContext;
|
||||
private WindowManager mWindowManager;
|
||||
private Rect mPositionRect;
|
||||
private int mOrientation;
|
||||
private Insets mInsets;
|
||||
|
||||
private int mBubbleSize;
|
||||
private int mBubbleBitmapSize;
|
||||
|
||||
private PointF mPinLocation;
|
||||
private PointF mRestingStackPosition;
|
||||
|
||||
private boolean mShowingInTaskbar;
|
||||
private @TaskbarPosition int mTaskbarPosition;
|
||||
private int mTaskbarIconSize;
|
||||
|
||||
public BubblePositioner(Context context, WindowManager windowManager) {
|
||||
mContext = context;
|
||||
mWindowManager = windowManager;
|
||||
update(Configuration.ORIENTATION_UNDEFINED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates orientation, available space, and inset information. Call this when config changes
|
||||
* occur or when added to a window.
|
||||
*/
|
||||
public void update(int orientation) {
|
||||
WindowMetrics windowMetrics = mWindowManager.getCurrentWindowMetrics();
|
||||
mPositionRect = new Rect(windowMetrics.getBounds());
|
||||
if (windowMetrics == null) {
|
||||
return;
|
||||
}
|
||||
WindowInsets metricInsets = windowMetrics.getWindowInsets();
|
||||
|
||||
Insets insets = metricInsets.getInsetsIgnoringVisibility(WindowInsets.Type.navigationBars()
|
||||
| WindowInsets.Type.statusBars()
|
||||
| WindowInsets.Type.displayCutout());
|
||||
update(orientation, insets, windowMetrics.getBounds());
|
||||
|
||||
if (BubbleDebugConfig.DEBUG_POSITIONER) {
|
||||
Log.w(TAG, "update positioner:"
|
||||
+ " landscape= " + (orientation == Configuration.ORIENTATION_LANDSCAPE)
|
||||
+ " insets: " + insets
|
||||
+ " bounds: " + windowMetrics.getBounds()
|
||||
+ " showingInTaskbar: " + mShowingInTaskbar);
|
||||
}
|
||||
updateInternal(orientation, insets, windowMetrics.getBounds());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void update(int orientation, Insets insets, Rect bounds) {
|
||||
public void updateInternal(int orientation, Insets insets, Rect bounds) {
|
||||
mOrientation = orientation;
|
||||
mInsets = insets;
|
||||
|
||||
@@ -63,27 +117,156 @@ public class BubblePositioner {
|
||||
mPositionRect.top += mInsets.top;
|
||||
mPositionRect.right -= mInsets.right;
|
||||
mPositionRect.bottom -= mInsets.bottom;
|
||||
|
||||
Resources res = mContext.getResources();
|
||||
mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
|
||||
mBubbleBitmapSize = res.getDimensionPixelSize(R.dimen.bubble_bitmap_size);
|
||||
adjustForTaskbar();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a rect of available screen space for displaying bubbles in the correct orientation,
|
||||
* accounting for system bars and cutouts.
|
||||
* Updates position information to account for taskbar state.
|
||||
*
|
||||
* @param taskbarPosition which position the taskbar is displayed in.
|
||||
* @param showingInTaskbar whether the taskbar is being shown.
|
||||
*/
|
||||
public void updateForTaskbar(int iconSize,
|
||||
@TaskbarPosition int taskbarPosition, boolean showingInTaskbar) {
|
||||
mShowingInTaskbar = showingInTaskbar;
|
||||
mTaskbarIconSize = iconSize;
|
||||
mTaskbarPosition = taskbarPosition;
|
||||
update(mOrientation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Taskbar insets appear as navigationBar insets, however, unlike navigationBar this should
|
||||
* not inset bubbles UI as bubbles floats above the taskbar. This adjust the available space
|
||||
* and insets to account for the taskbar.
|
||||
*/
|
||||
// TODO(b/171559950): When the insets are reported correctly we can remove this logic
|
||||
private void adjustForTaskbar() {
|
||||
// When bar is showing on edges... subtract that inset because we appear on top
|
||||
if (mShowingInTaskbar && mTaskbarPosition != TASKBAR_POSITION_BOTTOM) {
|
||||
WindowInsets metricInsets = mWindowManager.getCurrentWindowMetrics().getWindowInsets();
|
||||
Insets navBarInsets = metricInsets.getInsetsIgnoringVisibility(
|
||||
WindowInsets.Type.navigationBars());
|
||||
int newInsetLeft = mInsets.left;
|
||||
int newInsetRight = mInsets.right;
|
||||
if (mTaskbarPosition == TASKBAR_POSITION_LEFT) {
|
||||
mPositionRect.left -= navBarInsets.left;
|
||||
newInsetLeft -= navBarInsets.left;
|
||||
} else if (mTaskbarPosition == TASKBAR_POSITION_RIGHT) {
|
||||
mPositionRect.right += navBarInsets.right;
|
||||
newInsetRight -= navBarInsets.right;
|
||||
}
|
||||
mInsets = Insets.of(newInsetLeft, mInsets.top, newInsetRight, mInsets.bottom);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a rect of available screen space accounting for orientation, system bars and cutouts.
|
||||
* Does not account for IME.
|
||||
*/
|
||||
public Rect getAvailableRect() {
|
||||
return mPositionRect;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current orientation.
|
||||
*/
|
||||
public int getOrientation() {
|
||||
return mOrientation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the relevant insets (status bar, nav bar, cutouts).
|
||||
* @return the relevant insets (status bar, nav bar, cutouts). If taskbar is showing, its
|
||||
* inset is not included here.
|
||||
*/
|
||||
public Insets getInsets() {
|
||||
return mInsets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the device is in landscape orientation.
|
||||
*/
|
||||
public boolean isLandscape() {
|
||||
return mOrientation == Configuration.ORIENTATION_LANDSCAPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates how bubbles appear when expanded.
|
||||
*
|
||||
* When false, bubbles display at the top of the screen with the expanded view
|
||||
* below them. When true, bubbles display at the edges of the screen with the expanded view
|
||||
* to the left or right side.
|
||||
*/
|
||||
public boolean showBubblesVertically() {
|
||||
return mOrientation == Configuration.ORIENTATION_LANDSCAPE
|
||||
|| mShowingInTaskbar;
|
||||
}
|
||||
|
||||
/** Size of the bubble account for badge & dot. */
|
||||
public int getBubbleSize() {
|
||||
int bsize = (mShowingInTaskbar && mTaskbarIconSize > 0)
|
||||
? mTaskbarIconSize
|
||||
: mBubbleSize;
|
||||
return bsize;
|
||||
}
|
||||
|
||||
/** Size of the bitmap within the bubble */
|
||||
public int getBubbleBitmapSize() {
|
||||
float size = (mShowingInTaskbar && mTaskbarIconSize > 0)
|
||||
? (mTaskbarIconSize * BUBBLE_BITMAP_SIZE_PERCENT)
|
||||
: mBubbleBitmapSize;
|
||||
return (int) size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the stack's most recent position along the edge of the screen. This is saved when the
|
||||
* last bubble is removed, so that the stack can be restored in its previous position.
|
||||
*/
|
||||
public void setRestingPosition(PointF position) {
|
||||
if (mRestingStackPosition == null) {
|
||||
mRestingStackPosition = new PointF(position);
|
||||
} else {
|
||||
mRestingStackPosition.set(position);
|
||||
}
|
||||
}
|
||||
|
||||
/** The position the bubble stack should rest at when collapsed. */
|
||||
public PointF getRestingPosition() {
|
||||
if (mPinLocation != null) {
|
||||
return mPinLocation;
|
||||
}
|
||||
if (mRestingStackPosition == null) {
|
||||
return getDefaultStartPosition();
|
||||
}
|
||||
return mRestingStackPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the stack position to use if we don't have a saved location or if user education
|
||||
* is being shown.
|
||||
*/
|
||||
public PointF getDefaultStartPosition() {
|
||||
// Start on the left if we're in LTR, right otherwise.
|
||||
final boolean startOnLeft =
|
||||
mContext.getResources().getConfiguration().getLayoutDirection()
|
||||
!= View.LAYOUT_DIRECTION_RTL;
|
||||
final float startingVerticalOffset = mContext.getResources().getDimensionPixelOffset(
|
||||
R.dimen.bubble_stack_starting_offset_y);
|
||||
// TODO: placement bug here because mPositionRect doesn't handle the overhanging edge
|
||||
return new BubbleStackView.RelativeStackPosition(
|
||||
startOnLeft,
|
||||
startingVerticalOffset / mPositionRect.height())
|
||||
.getAbsolutePositionInRegion(new RectF(mPositionRect));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the bubble stack is pinned to the taskbar.
|
||||
*/
|
||||
public boolean showingInTaskbar() {
|
||||
return mShowingInTaskbar;
|
||||
}
|
||||
|
||||
/**
|
||||
* In some situations bubbles will be pinned to a specific onscreen location. This sets the
|
||||
* location to anchor the stack to.
|
||||
*/
|
||||
public void setPinnedLocation(PointF point) {
|
||||
mPinLocation = point;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ import android.annotation.SuppressLint;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.ColorMatrix;
|
||||
@@ -701,7 +700,6 @@ public class BubbleStackView extends FrameLayout
|
||||
}
|
||||
};
|
||||
|
||||
@Nullable
|
||||
private BubbleOverflow mBubbleOverflow;
|
||||
private StackEducationView mStackEduView;
|
||||
private ManageEducationView mManageEduView;
|
||||
@@ -818,11 +816,12 @@ public class BubbleStackView extends FrameLayout
|
||||
setFocusable(true);
|
||||
mBubbleContainer.bringToFront();
|
||||
|
||||
mBubbleOverflow = new BubbleOverflow(getContext(), bubbleController, this);
|
||||
mBubbleOverflow = new BubbleOverflow(getContext(), mPositioner);
|
||||
mBubbleOverflow.initialize(mBubbleController);
|
||||
mBubbleContainer.addView(mBubbleOverflow.getIconView(),
|
||||
mBubbleContainer.getChildCount() /* index */,
|
||||
new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
new FrameLayout.LayoutParams(mPositioner.getBubbleSize(),
|
||||
mPositioner.getBubbleSize()));
|
||||
updateOverflow();
|
||||
mBubbleOverflow.getIconView().setOnClickListener((View v) -> {
|
||||
setSelectedBubble(mBubbleOverflow);
|
||||
@@ -1067,7 +1066,7 @@ public class BubbleStackView extends FrameLayout
|
||||
mStackEduView = new StackEducationView(mContext);
|
||||
addView(mStackEduView);
|
||||
}
|
||||
return mStackEduView.show(mStackAnimationController.getStartPosition());
|
||||
return mStackEduView.show(mPositioner.getDefaultStartPosition());
|
||||
}
|
||||
|
||||
private void updateUserEdu() {
|
||||
@@ -1093,7 +1092,7 @@ public class BubbleStackView extends FrameLayout
|
||||
addView(mFlyout, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
|
||||
}
|
||||
|
||||
void updateFlyout(float fontScale) {
|
||||
void updateFontScale(float fontScale) {
|
||||
mFlyout.updateFontSize(fontScale);
|
||||
}
|
||||
|
||||
@@ -1130,7 +1129,9 @@ public class BubbleStackView extends FrameLayout
|
||||
Resources res = getContext().getResources();
|
||||
mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
|
||||
|
||||
mRelativeStackPositionBeforeRotation = mStackAnimationController.getRelativeStackPosition();
|
||||
mRelativeStackPositionBeforeRotation = new RelativeStackPosition(
|
||||
mPositioner.getRestingPosition(),
|
||||
mStackAnimationController.getAllowableStackPositionRegion());
|
||||
mManageMenu.setVisibility(View.INVISIBLE);
|
||||
mShowingManage = false;
|
||||
|
||||
@@ -1157,7 +1158,7 @@ public class BubbleStackView extends FrameLayout
|
||||
|
||||
Resources res = getContext().getResources();
|
||||
mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
|
||||
mBubbleSize = getResources().getDimensionPixelSize(R.dimen.individual_bubble_size);
|
||||
mBubbleSize = mPositioner.getBubbleSize();
|
||||
for (Bubble b : mBubbleData.getBubbles()) {
|
||||
if (b.getIconView() == null) {
|
||||
Log.d(TAG, "Display size changed. Icon null: " + b);
|
||||
@@ -1165,6 +1166,7 @@ public class BubbleStackView extends FrameLayout
|
||||
}
|
||||
b.getIconView().setLayoutParams(new LayoutParams(mBubbleSize, mBubbleSize));
|
||||
}
|
||||
mBubbleOverflow.getIconView().setLayoutParams(new LayoutParams(mBubbleSize, mBubbleSize));
|
||||
mExpandedAnimationController.updateResources();
|
||||
mStackAnimationController.updateResources();
|
||||
mDismissView.updateResources();
|
||||
@@ -1199,8 +1201,8 @@ public class BubbleStackView extends FrameLayout
|
||||
super.onDetachedFromWindow();
|
||||
getViewTreeObserver().removeOnPreDrawListener(mViewUpdater);
|
||||
getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
|
||||
if (mBubbleOverflow != null && mBubbleOverflow.getExpandedView() != null) {
|
||||
mBubbleOverflow.getExpandedView().cleanUpExpandedState();
|
||||
if (mBubbleOverflow != null) {
|
||||
mBubbleOverflow.cleanUpExpandedState();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1390,8 +1392,7 @@ public class BubbleStackView extends FrameLayout
|
||||
|
||||
if (getBubbleCount() == 0 && shouldShowStackEdu()) {
|
||||
// Override the default stack position if we're showing user education.
|
||||
mStackAnimationController.setStackPosition(
|
||||
mStackAnimationController.getStartPosition());
|
||||
mStackAnimationController.setStackPosition(mPositioner.getDefaultStartPosition());
|
||||
}
|
||||
|
||||
if (getBubbleCount() == 0) {
|
||||
@@ -1410,7 +1411,8 @@ public class BubbleStackView extends FrameLayout
|
||||
bubble.getIconView().setOnTouchListener(mBubbleTouchListener);
|
||||
|
||||
mBubbleContainer.addView(bubble.getIconView(), 0,
|
||||
new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
|
||||
new FrameLayout.LayoutParams(mPositioner.getBubbleSize(),
|
||||
mPositioner.getBubbleSize()));
|
||||
animateInFlyoutForBubble(bubble);
|
||||
requestUpdate();
|
||||
logBubbleEvent(bubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__POSTED);
|
||||
@@ -1614,6 +1616,11 @@ public class BubbleStackView extends FrameLayout
|
||||
mBubbleController.hideCurrentInputMethod();
|
||||
}
|
||||
|
||||
/** Set the stack position to whatever the positioner says. */
|
||||
void updateStackPosition() {
|
||||
mStackAnimationController.setStackPosition(mPositioner.getRestingPosition());
|
||||
}
|
||||
|
||||
private void beforeExpandedViewAnimation() {
|
||||
mIsExpansionAnimating = true;
|
||||
hideFlyoutImmediate();
|
||||
@@ -1629,8 +1636,7 @@ public class BubbleStackView extends FrameLayout
|
||||
|
||||
private void animateExpansion() {
|
||||
cancelDelayedExpandCollapseSwitchAnimations();
|
||||
final boolean isLandscape =
|
||||
mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE;
|
||||
final boolean showVertically = mPositioner.showBubblesVertically();
|
||||
mIsExpanded = true;
|
||||
if (mStackEduView != null) {
|
||||
mStackEduView.hide(true /* fromExpansion */);
|
||||
@@ -1650,14 +1656,19 @@ public class BubbleStackView extends FrameLayout
|
||||
mExpandedViewContainer.setTranslationY(getExpandedViewY());
|
||||
mExpandedViewContainer.setAlpha(1f);
|
||||
|
||||
// X-value of the bubble we're expanding, once it's settled in its row.
|
||||
int index;
|
||||
if (mExpandedBubble != null && BubbleOverflow.KEY.equals(mExpandedBubble.getKey())) {
|
||||
index = mBubbleData.getBubbles().size();
|
||||
} else {
|
||||
index = getBubbleIndex(mExpandedBubble);
|
||||
}
|
||||
// Position of the bubble we're expanding, once it's settled in its row.
|
||||
final float bubbleWillBeAt =
|
||||
mExpandedAnimationController.getBubbleXOrYForOrientation(
|
||||
mBubbleData.getBubbles().indexOf(mExpandedBubble));
|
||||
mExpandedAnimationController.getBubbleXOrYForOrientation(index);
|
||||
|
||||
// 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 relevantStackPosition = isLandscape
|
||||
final float relevantStackPosition = showVertically
|
||||
? mStackAnimationController.getStackPosition().y
|
||||
: mStackAnimationController.getStackPosition().x;
|
||||
final float distanceAnimated = Math.abs(bubbleWillBeAt - relevantStackPosition);
|
||||
@@ -1674,7 +1685,7 @@ public class BubbleStackView extends FrameLayout
|
||||
}
|
||||
|
||||
// Set the pivot point for the scale, so the expanded view animates out from the bubble.
|
||||
if (isLandscape) {
|
||||
if (showVertically) {
|
||||
float pivotX;
|
||||
float pivotY = bubbleWillBeAt + mBubbleSize / 2f;
|
||||
if (mStackOnLeftOrWillBe) {
|
||||
@@ -1709,7 +1720,7 @@ public class BubbleStackView extends FrameLayout
|
||||
if (mExpandedBubble == null || mExpandedBubble.getIconView() == null) {
|
||||
return;
|
||||
}
|
||||
float translation = isLandscape
|
||||
float translation = showVertically
|
||||
? mExpandedBubble.getIconView().getTranslationY()
|
||||
: mExpandedBubble.getIconView().getTranslationX();
|
||||
mExpandedViewContainerMatrix.postTranslate(
|
||||
@@ -1761,13 +1772,17 @@ public class BubbleStackView extends FrameLayout
|
||||
// We want to visually collapse into this bubble during the animation.
|
||||
final View expandingFromBubble = mExpandedBubble.getIconView();
|
||||
|
||||
int index;
|
||||
if (mExpandedBubble != null && BubbleOverflow.KEY.equals(mExpandedBubble.getKey())) {
|
||||
index = mBubbleData.getBubbles().size();
|
||||
} else {
|
||||
index = mBubbleData.getBubbles().indexOf(mExpandedBubble);
|
||||
}
|
||||
// Value the bubble is animating from (back into the stack).
|
||||
final float expandingFromBubbleAt =
|
||||
mExpandedAnimationController.getBubbleXOrYForOrientation(
|
||||
mBubbleData.getBubbles().indexOf(mExpandedBubble));
|
||||
final boolean isLandscape =
|
||||
mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE;
|
||||
if (isLandscape) {
|
||||
mExpandedAnimationController.getBubbleXOrYForOrientation(index);
|
||||
final boolean showVertically = mPositioner.showBubblesVertically();
|
||||
if (mPositioner.showBubblesVertically()) {
|
||||
float pivotX;
|
||||
float pivotY = expandingFromBubbleAt + mBubbleSize / 2f;
|
||||
if (mStackOnLeftOrWillBe) {
|
||||
@@ -1792,7 +1807,7 @@ public class BubbleStackView extends FrameLayout
|
||||
.addUpdateListener((target, values) -> {
|
||||
if (expandingFromBubble != null) {
|
||||
// Follow the bubble as it translates!
|
||||
if (isLandscape) {
|
||||
if (showVertically) {
|
||||
mExpandedViewContainerMatrix.postTranslate(
|
||||
0f, expandingFromBubble.getTranslationY()
|
||||
- expandingFromBubbleAt);
|
||||
@@ -1849,7 +1864,7 @@ public class BubbleStackView extends FrameLayout
|
||||
.spring(DynamicAnimation.SCALE_Y, 0f, mScaleOutSpringConfig)
|
||||
.withEndActions(this::releaseAnimatingOutBubbleBuffer);
|
||||
|
||||
if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
if (mPositioner.showBubblesVertically()) {
|
||||
float translationX = mStackAnimationController.isStackOnLeftSide()
|
||||
? mAnimatingOutSurfaceContainer.getTranslationX() + mBubbleSize * 2
|
||||
: mAnimatingOutSurfaceContainer.getTranslationX();
|
||||
@@ -1874,7 +1889,7 @@ public class BubbleStackView extends FrameLayout
|
||||
mExpandedViewContainer.setAlpha(1f);
|
||||
mExpandedViewContainer.setVisibility(View.VISIBLE);
|
||||
|
||||
if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
if (mPositioner.showBubblesVertically()) {
|
||||
float pivotX;
|
||||
float pivotY = expandingFromBubbleDestination + mBubbleSize / 2f;
|
||||
if (mStackOnLeftOrWillBe) {
|
||||
@@ -2180,7 +2195,7 @@ public class BubbleStackView extends FrameLayout
|
||||
*/
|
||||
float getExpandedViewY() {
|
||||
final int top = mPositioner.getAvailableRect().top;
|
||||
if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
if (mPositioner.showBubblesVertically()) {
|
||||
return top + mExpandedViewPadding;
|
||||
} else {
|
||||
return top + mBubbleSize + mBubblePaddingTop;
|
||||
@@ -2278,7 +2293,8 @@ public class BubbleStackView extends FrameLayout
|
||||
expandFlyoutAfterDelay /* onLayoutComplete */,
|
||||
mAfterFlyoutHidden,
|
||||
bubble.getIconView().getDotCenter(),
|
||||
!bubble.showDot());
|
||||
!bubble.showDot(),
|
||||
mPositioner);
|
||||
}
|
||||
mFlyout.bringToFront();
|
||||
});
|
||||
@@ -2550,7 +2566,7 @@ public class BubbleStackView extends FrameLayout
|
||||
Insets insets = mPositioner.getInsets();
|
||||
int leftPadding = insets.left + mExpandedViewPadding;
|
||||
int rightPadding = insets.right + mExpandedViewPadding;
|
||||
if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
if (mPositioner.showBubblesVertically()) {
|
||||
if (!mStackAnimationController.isStackOnLeftSide()) {
|
||||
rightPadding += mPointerHeight + mBubbleSize;
|
||||
} else {
|
||||
@@ -2581,7 +2597,9 @@ public class BubbleStackView extends FrameLayout
|
||||
bv.setZ((mMaxBubbles * mBubbleElevation) - i);
|
||||
|
||||
if (mIsExpanded) {
|
||||
bv.showDotAndBadge(false /* onLeft */);
|
||||
// If we're not displaying vertically, we always show the badge on the left.
|
||||
boolean onLeft = mPositioner.showBubblesVertically() && !mStackOnLeftOrWillBe;
|
||||
bv.showDotAndBadge(onLeft);
|
||||
} else if (i == 0) {
|
||||
bv.showDotAndBadge(!mStackOnLeftOrWillBe);
|
||||
} else {
|
||||
@@ -2599,7 +2617,7 @@ public class BubbleStackView extends FrameLayout
|
||||
return;
|
||||
}
|
||||
float bubblePosition = mExpandedAnimationController.getBubbleXOrYForOrientation(index);
|
||||
if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
if (mPositioner.showBubblesVertically()) {
|
||||
float x = mStackOnLeftOrWillBe
|
||||
? mPositioner.getAvailableRect().left
|
||||
: mPositioner.getAvailableRect().right
|
||||
@@ -2661,21 +2679,11 @@ public class BubbleStackView extends FrameLayout
|
||||
.floatValue();
|
||||
}
|
||||
|
||||
/** Set the start position of the bubble stack. */
|
||||
public void setStackStartPosition(RelativeStackPosition position) {
|
||||
mStackAnimationController.setStackStartPosition(position);
|
||||
}
|
||||
|
||||
/** @return the position of the bubble stack. */
|
||||
public PointF getStackPosition() {
|
||||
return mStackAnimationController.getStackPosition();
|
||||
}
|
||||
|
||||
/** @return the relative position of the bubble stack. */
|
||||
public RelativeStackPosition getRelativeStackPosition() {
|
||||
return mStackAnimationController.getRelativeStackPosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the bubble UI event.
|
||||
*
|
||||
|
||||
@@ -134,6 +134,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
|
||||
LayoutInflater inflater = LayoutInflater.from(c);
|
||||
info.imageView = (BadgedImageView) inflater.inflate(
|
||||
R.layout.bubble_view, stackView, false /* attachToRoot */);
|
||||
info.imageView.initialize(controller.getPositioner());
|
||||
|
||||
info.expandedView = (BubbleExpandedView) inflater.inflate(
|
||||
R.layout.bubble_expanded_view, stackView, false /* attachToRoot */);
|
||||
|
||||
@@ -84,7 +84,11 @@ public class ExpandedAnimationController
|
||||
private float mBubbleSizePx;
|
||||
/** Max number of bubbles shown in row above expanded view. */
|
||||
private int mBubblesMaxRendered;
|
||||
|
||||
/** Max amount of space to have between bubbles when expanded. */
|
||||
private int mBubblesMaxSpace;
|
||||
/** Amount of space between the bubbles when expanded. */
|
||||
private float mSpaceBetweenBubbles;
|
||||
/** Whether the expand / collapse animation is running. */
|
||||
private boolean mAnimatingExpand = false;
|
||||
|
||||
/**
|
||||
@@ -205,8 +209,17 @@ public class ExpandedAnimationController
|
||||
mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
|
||||
mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
|
||||
mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
|
||||
mBubbleSizePx = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
|
||||
mBubbleSizePx = mPositioner.getBubbleSize();
|
||||
mBubblesMaxRendered = res.getInteger(R.integer.bubbles_max_rendered);
|
||||
mBubblesMaxSpace = res.getDimensionPixelSize(R.dimen.bubble_max_spacing);
|
||||
final float availableSpace = mPositioner.isLandscape()
|
||||
? mPositioner.getAvailableRect().height()
|
||||
: mPositioner.getAvailableRect().width();
|
||||
final float spaceForMaxBubbles = (mExpandedViewPadding * 2)
|
||||
+ (mBubblesMaxRendered + 1) * mBubbleSizePx;
|
||||
float spaceBetweenBubbles =
|
||||
(availableSpace - spaceForMaxBubbles) / mBubblesMaxRendered;
|
||||
mSpaceBetweenBubbles = Math.min(spaceBetweenBubbles, mBubblesMaxSpace);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -250,13 +263,15 @@ public class ExpandedAnimationController
|
||||
final Path path = new Path();
|
||||
path.moveTo(bubble.getTranslationX(), bubble.getTranslationY());
|
||||
|
||||
final float expandedY = getExpandedY();
|
||||
final float expandedY = mPositioner.showBubblesVertically()
|
||||
? getBubbleXOrYForOrientation(index)
|
||||
: getExpandedY();
|
||||
if (expanding) {
|
||||
// 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.
|
||||
if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
if (mPositioner.showBubblesVertically()) {
|
||||
Rect availableRect = mPositioner.getAvailableRect();
|
||||
boolean onLeft = mCollapsePoint != null
|
||||
&& mCollapsePoint.x < (availableRect.width() / 2f);
|
||||
@@ -422,6 +437,9 @@ public class ExpandedAnimationController
|
||||
* bubbles to accommodate it if it was previously dragged out past the threshold.
|
||||
*/
|
||||
public void snapBubbleBack(View bubbleView, float velX, float velY) {
|
||||
if (mLayout == null) {
|
||||
return;
|
||||
}
|
||||
final int index = mLayout.indexOfChild(bubbleView);
|
||||
|
||||
animationForChildAtIndex(index)
|
||||
@@ -580,7 +598,7 @@ public class ExpandedAnimationController
|
||||
return;
|
||||
}
|
||||
|
||||
if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
if (mPositioner.showBubblesVertically()) {
|
||||
Rect availableRect = mPositioner.getAvailableRect();
|
||||
boolean onLeft = mCollapsePoint != null
|
||||
&& mCollapsePoint.x < (availableRect.width() / 2f);
|
||||
@@ -613,23 +631,15 @@ public class ExpandedAnimationController
|
||||
if (mLayout == null) {
|
||||
return 0;
|
||||
}
|
||||
final float positionInBar = index * (mBubbleSizePx + mSpaceBetweenBubbles);
|
||||
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 boolean isLandscape = mPositioner.showBubblesVertically();
|
||||
final float expandedStackSize = (mLayout.getChildCount() * mBubbleSizePx)
|
||||
+ ((mLayout.getChildCount() - 1) * spaceBetweenBubbles);
|
||||
+ ((mLayout.getChildCount() - 1) * mSpaceBetweenBubbles);
|
||||
final float centerPosition = isLandscape
|
||||
? availableRect.centerY()
|
||||
: availableRect.centerX();
|
||||
final float rowStart = centerPosition - (expandedStackSize / 2f);
|
||||
final float positionInBar = index * (mBubbleSizePx + spaceBetweenBubbles);
|
||||
return rowStart + positionInBar;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,12 +133,6 @@ public class StackAnimationController extends
|
||||
/** Whether or not the stack's start position has been set. */
|
||||
private boolean mStackMovedToStartPosition = false;
|
||||
|
||||
/**
|
||||
* The stack's most recent position along the edge of the screen. This is saved when the last
|
||||
* bubble is removed, so that the stack can be restored in its previous position.
|
||||
*/
|
||||
private PointF mRestingStackPosition;
|
||||
|
||||
/** The height of the most recently visible IME. */
|
||||
private float mImeHeight = 0f;
|
||||
|
||||
@@ -434,6 +428,9 @@ public class StackAnimationController extends
|
||||
* Where the stack would be if it were snapped to the nearest horizontal edge (left or right).
|
||||
*/
|
||||
public PointF getStackPositionAlongNearestHorizontalEdge() {
|
||||
if (mPositioner.showingInTaskbar()) {
|
||||
return mPositioner.getRestingPosition();
|
||||
}
|
||||
final PointF stackPos = getStackPosition();
|
||||
final boolean onLeft = mLayout.isFirstChildXLeftOfCenter(stackPos.x);
|
||||
final RectF bounds = getAllowableStackPositionRegion();
|
||||
@@ -447,7 +444,7 @@ public class StackAnimationController extends
|
||||
pw.println("StackAnimationController state:");
|
||||
pw.print(" isActive: "); pw.println(isActiveController());
|
||||
pw.print(" restingStackPos: ");
|
||||
pw.println(mRestingStackPosition != null ? mRestingStackPosition.toString() : "null");
|
||||
pw.println(mPositioner.getRestingPosition().toString());
|
||||
pw.print(" currentStackPos: "); pw.println(mStackPosition.toString());
|
||||
pw.print(" isMovingFromFlinging: "); pw.println(mIsMovingFromFlinging);
|
||||
pw.print(" withinDismiss: "); pw.println(isStackStuckToTarget());
|
||||
@@ -501,7 +498,7 @@ public class StackAnimationController extends
|
||||
|
||||
.addEndListener((animation, canceled, endValue, endVelocity) -> {
|
||||
if (!canceled) {
|
||||
mRestingStackPosition.set(mStackPosition);
|
||||
mPositioner.setRestingPosition(mStackPosition);
|
||||
|
||||
springFirstBubbleWithStackFollowing(property, spring, endVelocity,
|
||||
finalPosition != null
|
||||
@@ -679,7 +676,7 @@ public class StackAnimationController extends
|
||||
// resting position - the touch location is not a valid resting
|
||||
// position. We'll set this when the stack springs to the left or
|
||||
// right side of the screen after the touch gesture ends.
|
||||
mRestingStackPosition.set(mStackPosition);
|
||||
mPositioner.setRestingPosition(mStackPosition);
|
||||
}
|
||||
|
||||
if (after != null) {
|
||||
@@ -772,10 +769,9 @@ public class StackAnimationController extends
|
||||
if (getBubbleCount() > 0) {
|
||||
animationForChildAtIndex(0).translationX(mStackPosition.x).start();
|
||||
} else {
|
||||
// TODO: still needed with positioner?
|
||||
// When all children are removed ensure stack position is sane
|
||||
setStackPosition(mRestingStackPosition == null
|
||||
? getStartPosition()
|
||||
: mRestingStackPosition);
|
||||
mPositioner.setRestingPosition(mPositioner.getRestingPosition());
|
||||
|
||||
// Remove the stack from the coordinator since we don't have any bubbles and aren't
|
||||
// visible.
|
||||
@@ -848,8 +844,8 @@ public class StackAnimationController extends
|
||||
mSwapAnimationOffset = res.getDimensionPixelSize(R.dimen.bubble_swap_animation_offset);
|
||||
mMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered);
|
||||
mElevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
|
||||
mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
|
||||
mBubbleBitmapSize = res.getDimensionPixelSize(R.dimen.bubble_bitmap_size);
|
||||
mBubbleSize = mPositioner.getBubbleSize();
|
||||
mBubbleBitmapSize = mPositioner.getBubbleBitmapSize();
|
||||
mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
|
||||
mBubbleOffscreen = res.getDimensionPixelSize(R.dimen.bubble_stack_offscreen);
|
||||
}
|
||||
@@ -873,9 +869,8 @@ public class StackAnimationController extends
|
||||
// Post to ensure that the layout's width and height have been calculated.
|
||||
mLayout.setVisibility(View.INVISIBLE);
|
||||
mLayout.post(() -> {
|
||||
setStackPosition(mRestingStackPosition == null
|
||||
? getStartPosition()
|
||||
: mRestingStackPosition);
|
||||
setStackPosition(mPositioner.getRestingPosition());
|
||||
|
||||
mStackMovedToStartPosition = true;
|
||||
mLayout.setVisibility(View.VISIBLE);
|
||||
|
||||
@@ -919,11 +914,7 @@ public class StackAnimationController extends
|
||||
Log.d(TAG, String.format("Setting position to (%f, %f).", pos.x, pos.y));
|
||||
mStackPosition.set(pos.x, pos.y);
|
||||
|
||||
if (mRestingStackPosition == null) {
|
||||
mRestingStackPosition = new PointF();
|
||||
}
|
||||
|
||||
mRestingStackPosition.set(mStackPosition);
|
||||
mPositioner.setRestingPosition(mStackPosition);
|
||||
|
||||
// If we're not the active controller, we don't want to physically move the bubble views.
|
||||
if (isActiveController()) {
|
||||
|
||||
@@ -19,7 +19,9 @@ package com.android.wm.shell.bubbles;
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertNotSame;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PointF;
|
||||
@@ -36,7 +38,7 @@ import com.android.wm.shell.ShellTestCase;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@SmallTest
|
||||
@@ -48,11 +50,16 @@ public class BubbleFlyoutViewTest extends ShellTestCase {
|
||||
private TextView mSenderName;
|
||||
private float[] mDotCenter = new float[2];
|
||||
private Bubble.FlyoutMessage mFlyoutMessage;
|
||||
@Mock
|
||||
private BubblePositioner mPositioner;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
when(mPositioner.getBubbleBitmapSize()).thenReturn(40);
|
||||
when(mPositioner.getBubbleSize()).thenReturn(60);
|
||||
|
||||
mFlyoutMessage = new Bubble.FlyoutMessage();
|
||||
mFlyoutMessage.senderName = "Josh";
|
||||
mFlyoutMessage.message = "Hello";
|
||||
@@ -70,7 +77,8 @@ public class BubbleFlyoutViewTest extends ShellTestCase {
|
||||
mFlyout.setupFlyoutStartingAsDot(
|
||||
mFlyoutMessage,
|
||||
new PointF(100, 100), 500, true, Color.WHITE, null, null, mDotCenter,
|
||||
false);
|
||||
false,
|
||||
mPositioner);
|
||||
mFlyout.setVisibility(View.VISIBLE);
|
||||
|
||||
assertEquals("Hello", mFlyoutText.getText());
|
||||
@@ -80,10 +88,11 @@ public class BubbleFlyoutViewTest extends ShellTestCase {
|
||||
|
||||
@Test
|
||||
public void testFlyoutHide_runsCallback() {
|
||||
Runnable after = Mockito.mock(Runnable.class);
|
||||
Runnable after = mock(Runnable.class);
|
||||
mFlyout.setupFlyoutStartingAsDot(mFlyoutMessage,
|
||||
new PointF(100, 100), 500, true, Color.WHITE, null, after, mDotCenter,
|
||||
false);
|
||||
false,
|
||||
mPositioner);
|
||||
mFlyout.hideFlyout();
|
||||
|
||||
verify(after).run();
|
||||
@@ -93,7 +102,8 @@ public class BubbleFlyoutViewTest extends ShellTestCase {
|
||||
public void testSetCollapsePercent() {
|
||||
mFlyout.setupFlyoutStartingAsDot(mFlyoutMessage,
|
||||
new PointF(100, 100), 500, true, Color.WHITE, null, null, mDotCenter,
|
||||
false);
|
||||
false,
|
||||
mPositioner);
|
||||
mFlyout.setVisibility(View.VISIBLE);
|
||||
|
||||
mFlyout.setCollapsePercent(1f);
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.wm.shell.bubbles;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Rect;
|
||||
import android.view.WindowManager;
|
||||
|
||||
public class TestableBubblePositioner extends BubblePositioner {
|
||||
|
||||
public TestableBubblePositioner(Context context,
|
||||
WindowManager windowManager) {
|
||||
super(context, windowManager);
|
||||
|
||||
updateInternal(Configuration.ORIENTATION_PORTRAIT,
|
||||
Insets.of(0, 0, 0, 0),
|
||||
new Rect(0, 0, 500, 1000));
|
||||
}
|
||||
}
|
||||
@@ -64,7 +64,7 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC
|
||||
super.setUp();
|
||||
|
||||
BubblePositioner positioner = new BubblePositioner(getContext(), mock(WindowManager.class));
|
||||
positioner.update(Configuration.ORIENTATION_PORTRAIT,
|
||||
positioner.updateInternal(Configuration.ORIENTATION_PORTRAIT,
|
||||
Insets.of(0, 0, 0, 0),
|
||||
new Rect(0, 0, mDisplayWidth, mDisplayHeight));
|
||||
mExpandedController = new ExpandedAnimationController(positioner, mExpandedViewPadding,
|
||||
|
||||
@@ -27,6 +27,7 @@ import static org.mockito.Mockito.verify;
|
||||
import android.graphics.PointF;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.dynamicanimation.animation.DynamicAnimation;
|
||||
@@ -34,7 +35,7 @@ import androidx.dynamicanimation.animation.SpringForce;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.wm.shell.R;
|
||||
import com.android.wm.shell.bubbles.BubblePositioner;
|
||||
import com.android.wm.shell.bubbles.TestableBubblePositioner;
|
||||
import com.android.wm.shell.common.FloatingContentCoordinator;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -310,7 +311,7 @@ public class StackAnimationControllerTest extends PhysicsAnimationLayoutTestCase
|
||||
super(floatingContentCoordinator,
|
||||
bubbleCountSupplier,
|
||||
onBubbleAnimatedOutAction,
|
||||
mock(BubblePositioner.class));
|
||||
new TestableBubblePositioner(mContext, mock(WindowManager.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -45,9 +45,6 @@ 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;
|
||||
@@ -99,7 +96,6 @@ import com.android.wm.shell.bubbles.BubbleData;
|
||||
import com.android.wm.shell.bubbles.BubbleDataRepository;
|
||||
import com.android.wm.shell.bubbles.BubbleEntry;
|
||||
import com.android.wm.shell.bubbles.BubbleLogger;
|
||||
import com.android.wm.shell.bubbles.BubblePositioner;
|
||||
import com.android.wm.shell.bubbles.BubbleStackView;
|
||||
import com.android.wm.shell.bubbles.Bubbles;
|
||||
import com.android.wm.shell.common.FloatingContentCoordinator;
|
||||
@@ -206,8 +202,8 @@ public class BubblesTest extends SysuiTestCase {
|
||||
private WindowManagerShellWrapper mWindowManagerShellWrapper;
|
||||
@Mock
|
||||
private BubbleLogger mBubbleLogger;
|
||||
@Mock
|
||||
private BubblePositioner mPositioner;
|
||||
|
||||
private TestableBubblePositioner mPositioner;
|
||||
|
||||
private BubbleData mBubbleData;
|
||||
|
||||
@@ -257,10 +253,7 @@ public class BubblesTest extends SysuiTestCase {
|
||||
|
||||
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));
|
||||
mPositioner = new TestableBubblePositioner(mContext, mWindowManager);
|
||||
|
||||
TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
|
||||
new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
|
||||
|
||||
@@ -42,9 +42,6 @@ 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.os.Handler;
|
||||
import android.os.PowerManager;
|
||||
@@ -92,7 +89,6 @@ import com.android.wm.shell.bubbles.BubbleData;
|
||||
import com.android.wm.shell.bubbles.BubbleDataRepository;
|
||||
import com.android.wm.shell.bubbles.BubbleEntry;
|
||||
import com.android.wm.shell.bubbles.BubbleLogger;
|
||||
import com.android.wm.shell.bubbles.BubblePositioner;
|
||||
import com.android.wm.shell.bubbles.BubbleStackView;
|
||||
import com.android.wm.shell.bubbles.Bubbles;
|
||||
import com.android.wm.shell.common.FloatingContentCoordinator;
|
||||
@@ -188,8 +184,8 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase {
|
||||
private WindowManagerShellWrapper mWindowManagerShellWrapper;
|
||||
@Mock
|
||||
private BubbleLogger mBubbleLogger;
|
||||
@Mock
|
||||
private BubblePositioner mPositioner;
|
||||
|
||||
private TestableBubblePositioner mPositioner;
|
||||
|
||||
private BubbleData mBubbleData;
|
||||
|
||||
@@ -226,10 +222,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase {
|
||||
|
||||
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));
|
||||
mPositioner = new TestableBubblePositioner(mContext, mWindowManager);
|
||||
|
||||
TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
|
||||
new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.wmshell;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Rect;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.android.wm.shell.bubbles.BubblePositioner;
|
||||
|
||||
public class TestableBubblePositioner extends BubblePositioner {
|
||||
|
||||
public TestableBubblePositioner(Context context,
|
||||
WindowManager windowManager) {
|
||||
super(context, windowManager);
|
||||
|
||||
updateInternal(Configuration.ORIENTATION_PORTRAIT,
|
||||
Insets.of(0, 0, 0, 0),
|
||||
new Rect(0, 0, 500, 1000));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user