Loading quickstep/res/layout/transient_taskbar.xml +0 −2 Original line number Diff line number Diff line Loading @@ -45,8 +45,6 @@ android:layout_gravity="bottom|end" android:layout_marginHorizontal="@dimen/transient_taskbar_bottom_margin" android:paddingTop="@dimen/bubblebar_pointer_visible_size" android:paddingEnd="@dimen/taskbar_icon_spacing" android:paddingStart="@dimen/taskbar_icon_spacing" android:visibility="gone" android:gravity="center" android:clipChildren="false" Loading quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt +7 −6 Original line number Diff line number Diff line Loading @@ -123,22 +123,23 @@ class BubbleBarBackground(context: Context, private var backgroundHeight: Float) ) // Create background path val backgroundPath = Path() val topOffset = backgroundHeight - bounds.height().toFloat() val radius = backgroundHeight / 2f val left = bounds.left + (if (anchorLeft) 0f else bounds.width().toFloat() - width) val right = bounds.left + (if (anchorLeft) width else bounds.width().toFloat()) val top = bounds.top + arrowVisibleHeight val top = bounds.top - topOffset + arrowVisibleHeight val bottom = bounds.top + bounds.height().toFloat() backgroundPath.addRoundRect(left, top, right, bottom, radius, radius, Path.Direction.CW) addArrowPathIfNeeded(backgroundPath) addArrowPathIfNeeded(backgroundPath, topOffset) // Draw background. canvas.drawPath(backgroundPath, fillPaint) canvas.drawPath(backgroundPath, strokePaint) canvas.restore() } private fun addArrowPathIfNeeded(sourcePath: Path) { private fun addArrowPathIfNeeded(sourcePath: Path, topOffset: Float) { if (!showingArrow || arrowHeightFraction <= 0) return val arrowPath = Path() RoundedArrowDrawable.addDownPointingRoundedTriangleToPath( Loading @@ -153,7 +154,7 @@ class BubbleBarBackground(context: Context, private var backgroundHeight: Float) arrowPath.transform(pathTransform) // shift to arrow position val arrowStart = bounds.left + arrowPositionX - (arrowWidth / 2f) val arrowTop = (1 - arrowHeightFraction) * arrowVisibleHeight val arrowTop = (1 - arrowHeightFraction) * arrowVisibleHeight - topOffset arrowPath.offset(arrowStart, arrowTop) // union with rectangle sourcePath.op(arrowPath, Path.Op.UNION) Loading @@ -180,7 +181,7 @@ class BubbleBarBackground(context: Context, private var backgroundHeight: Float) fillPaint.colorFilter = colorFilter } fun setHeight(newHeight: Float) { fun setBackgroundHeight(newHeight: Float) { backgroundHeight = newHeight } Loading quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java +169 −66 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.content.Context; Loading @@ -44,6 +45,7 @@ import androidx.dynamicanimation.animation.SpringForce; import com.android.launcher3.R; import com.android.launcher3.anim.SpringAnimationBuilder; import com.android.launcher3.util.DisplayController; import com.android.wm.shell.Flags; import com.android.wm.shell.common.bubbles.BubbleBarLocation; import java.util.List; Loading Loading @@ -85,6 +87,7 @@ public class BubbleBarView extends FrameLayout { private static final int MAX_VISIBLE_BUBBLES_COLLAPSED = 2; private static final int ARROW_POSITION_ANIMATION_DURATION_MS = 200; private static final int WIDTH_ANIMATION_DURATION_MS = 200; private static final int SCALE_ANIMATION_DURATION_MS = 200; private static final long FADE_OUT_ANIM_ALPHA_DURATION_MS = 50L; private static final long FADE_OUT_ANIM_ALPHA_DELAY_MS = 50L; Loading Loading @@ -135,13 +138,12 @@ public class BubbleBarView extends FrameLayout { private float mBubbleBarPadding; // The size of a bubble in the bar private float mIconSize; // The scale of bubble icons private float mIconScale = 1f; // The elevation of the bubbles within the bar private final float mBubbleElevation; private final float mDragElevation; private final int mPointerSize; private final Rect mTempBackgroundBounds = new Rect(); // Whether the bar is expanded (i.e. the bubble activity is being displayed). private boolean mIsBarExpanded = false; // The currently selected bubble view. Loading @@ -162,7 +164,8 @@ public class BubbleBarView extends FrameLayout { /** An animator used for scaling in a new bubble to the bubble bar while expanded. */ @Nullable private ValueAnimator mNewBubbleScaleInAnimator = null; @Nullable private ValueAnimator mScalePaddingAnimator; @Nullable private Animator mBubbleBarLocationAnimator = null; Loading Loading @@ -217,17 +220,10 @@ public class BubbleBarView extends FrameLayout { setBackgroundDrawable(mBubbleBarBackground); mWidthAnimator.setDuration(WIDTH_ANIMATION_DURATION_MS); mWidthAnimator.addUpdateListener(animation -> { updateBubblesLayoutProperties(mBubbleBarLocation); invalidate(); }); mWidthAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { addAnimationCallBacks(mWidthAnimator, /* onStart= */ () -> mBubbleBarBackground.showArrow(true), /* onEnd= */ () -> { mBubbleBarBackground.showArrow(mIsBarExpanded); if (!mIsBarExpanded && mReorderRunnable != null) { mReorderRunnable.run(); Loading @@ -242,17 +238,56 @@ public class BubbleBarView extends FrameLayout { mUpdateSelectedBubbleAfterCollapse.accept(firstBubble.getBubble().getKey()); } updateWidth(); }, /* onUpdate= */ animator -> { updateBubblesLayoutProperties(mBubbleBarLocation); invalidate(); }); } @Override public void onAnimationRepeat(Animator animation) { } @Override public void onAnimationStart(Animator animation) { mBubbleBarBackground.showArrow(true); /** * Animates icon sizes and spacing between icons and bubble bar borders. * * @param newIconSize new icon size * @param newBubbleBarPadding spacing between icons and bubble bar borders. */ public void animateBubbleBarIconSize(float newIconSize, float newBubbleBarPadding) { if (!isIconSizeOrPaddingUpdated(newIconSize, newBubbleBarPadding)) { return; } if (!Flags.animateBubbleSizeChange()) { setIconSizeAndPadding(newIconSize, newBubbleBarPadding); } if (mScalePaddingAnimator != null && mScalePaddingAnimator.isRunning()) { mScalePaddingAnimator.cancel(); } ValueAnimator scalePaddingAnimator = ValueAnimator.ofFloat(0f, 1f); scalePaddingAnimator.setDuration(SCALE_ANIMATION_DURATION_MS); boolean isPaddingUpdated = isPaddingUpdated(newBubbleBarPadding); boolean isIconSizeUpdated = isIconSizeUpdated(newIconSize); float initialScale = mIconScale; float initialPadding = mBubbleBarPadding; float targetScale = newIconSize / getScaledIconSize(); addAnimationCallBacks(scalePaddingAnimator, /* onStart= */ null, /* onEnd= */ () -> setIconSizeAndPadding(newIconSize, newBubbleBarPadding), /* onUpdate= */ animator -> { float transitionProgress = (float) animator.getAnimatedValue(); if (isIconSizeUpdated) { mIconScale = initialScale + (targetScale - initialScale) * transitionProgress; } if (isPaddingUpdated) { mBubbleBarPadding = initialPadding + (newBubbleBarPadding - initialPadding) * transitionProgress; } updateBubblesLayoutProperties(mBubbleBarLocation); invalidate(); }); scalePaddingAnimator.start(); mScalePaddingAnimator = scalePaddingAnimator; } @Override Loading @@ -267,28 +302,37 @@ public class BubbleBarView extends FrameLayout { } /** * Sets new icon size and spacing between icons and bubble bar borders. * Sets new icon sizes and newBubbleBarPadding between icons and bubble bar borders. * * @param newIconSize new icon size * @param spacing spacing between icons and bubble bar borders. * @param newBubbleBarPadding newBubbleBarPadding between icons and bubble bar borders. */ // TODO(b/335575529): animate bubble bar icons size change public void setIconSizeAndPadding(float newIconSize, float spacing) { public void setIconSizeAndPadding(float newIconSize, float newBubbleBarPadding) { // TODO(b/335457839): handle new bubble animation during the size change mBubbleBarPadding = spacing; if (!isIconSizeOrPaddingUpdated(newIconSize, newBubbleBarPadding)) { return; } mIconScale = 1f; mBubbleBarPadding = newBubbleBarPadding; mIconSize = newIconSize; int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View childView = getChildAt(i); childView.setScaleY(mIconScale); childView.setScaleY(mIconScale); FrameLayout.LayoutParams params = (LayoutParams) childView.getLayoutParams(); params.height = (int) mIconSize; params.width = (int) mIconSize; childView.setLayoutParams(params); } mBubbleBarBackground.setHeight(getBubbleBarExpandedHeight()); mBubbleBarBackground.setBackgroundHeight(getBubbleBarHeight()); updateLayoutParams(); } private float getScaledIconSize() { return mIconSize * mIconScale; } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); Loading Loading @@ -698,6 +742,11 @@ public class BubbleBarView extends FrameLayout { setLayoutParams(lp); } private float getBubbleBarHeight() { return mIsBarExpanded ? getBubbleBarExpandedHeight() : getBubbleBarCollapsedHeight(); } /** @return the horizontal margin between the bubble bar and the edge of the screen. */ int getHorizontalMargin() { LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams(); Loading @@ -714,7 +763,10 @@ public class BubbleBarView extends FrameLayout { final float expandedWidth = expandedWidth(); final float collapsedWidth = collapsedWidth(); int bubbleCount = getChildCount(); final float ty = (mBubbleBarBounds.height() - mIconSize) / 2f; float viewBottom = mBubbleBarBounds.height() + (isExpanded() ? mPointerSize : 0); float bubbleBarAnimatedTop = viewBottom - getBubbleBarHeight(); // When translating X & Y the scale is ignored, so need to deduct it from the translations final float ty = bubbleBarAnimatedTop + mBubbleBarPadding - getScaleIconShift(); final boolean animate = getVisibility() == VISIBLE; final boolean onLeft = bubbleBarLocation.isOnLeft(isLayoutRtl()); // elevation state is opposite to widthState - when expanded all icons are flat Loading @@ -729,8 +781,9 @@ public class BubbleBarView extends FrameLayout { bv.setDragTranslationX(0f); bv.setOffsetX(0f); bv.setScaleX(mIconScale); bv.setScaleY(mIconScale); bv.setTranslationY(ty); // the position of the bubble when the bar is fully expanded final float expandedX = getExpandedBubbleTranslationX(i, bubbleCount, onLeft); // the position of the bubble when the bar is fully collapsed Loading Loading @@ -795,21 +848,28 @@ public class BubbleBarView extends FrameLayout { mBubbleBarBackground.setArrowPosition(arrowPosition); mBubbleBarBackground.setArrowHeightFraction(widthState); mBubbleBarBackground.setWidth(interpolatedWidth); mBubbleBarBackground.setBackgroundHeight(getBubbleBarExpandedHeight()); } private float getScaleIconShift() { return (mIconSize - getScaledIconSize()) / 2; } private float getExpandedBubbleTranslationX(int bubbleIndex, int bubbleCount, boolean onLeft) { if (bubbleIndex < 0 || bubbleIndex >= bubbleCount) { return 0; } final float iconAndSpacing = mIconSize + mExpandedBarIconsSpacing; final float iconAndSpacing = getScaledIconSize() + mExpandedBarIconsSpacing; float translationX; if (mNewBubbleScaleInAnimator != null && mNewBubbleScaleInAnimator.isRunning()) { return getExpandedBubbleTranslationXDuringScaleAnimation( translationX = getExpandedBubbleTranslationXDuringScaleAnimation( bubbleIndex, bubbleCount, onLeft); } else if (onLeft) { return (bubbleCount - bubbleIndex - 1) * iconAndSpacing; translationX = mBubbleBarPadding + (bubbleCount - bubbleIndex - 1) * iconAndSpacing; } else { return bubbleIndex * iconAndSpacing; translationX = mBubbleBarPadding + bubbleIndex * iconAndSpacing; } return translationX - getScaleIconShift(); } /** Loading @@ -830,17 +890,17 @@ public class BubbleBarView extends FrameLayout { // compiler doesn't know that. return 0; } final float iconAndSpacing = mIconSize + mExpandedBarIconsSpacing; final float iconAndSpacing = getScaledIconSize() + mExpandedBarIconsSpacing; final float newBubbleScale = mNewBubbleScaleInAnimator.getAnimatedFraction(); // the new bubble is scaling in from the center, so we need to adjust its translation so // that the distance to the adjacent bubble scales at the same rate. final float pivotAdjustment = -(1 - newBubbleScale) * mIconSize / 2f; final float pivotAdjustment = -(1 - newBubbleScale) * getScaledIconSize() / 2f; if (onLeft) { if (bubbleIndex == 0) { // this is the animating bubble. use scaled spacing between it and the bubble to // its left return (bubbleCount - 1) * mIconSize return (bubbleCount - 1) * getScaledIconSize() + (bubbleCount - 2) * mExpandedBarIconsSpacing + newBubbleScale * mExpandedBarIconsSpacing + pivotAdjustment; Loading @@ -862,13 +922,16 @@ public class BubbleBarView extends FrameLayout { if (bubbleIndex < 0 || bubbleIndex >= bubbleCount) { return 0; } float translationX; if (onLeft) { // Shift the first bubble only if there are more bubbles in addition to overflow return bubbleIndex == 0 && bubbleCount > MAX_VISIBLE_BUBBLES_COLLAPSED ? mIconOverlapAmount : 0; translationX = mBubbleBarPadding + ( bubbleIndex == 0 && bubbleCount > MAX_VISIBLE_BUBBLES_COLLAPSED ? mIconOverlapAmount : 0); } else { return bubbleIndex == 0 ? 0 : mIconOverlapAmount; translationX = mBubbleBarPadding + (bubbleIndex == 0 ? 0 : mIconOverlapAmount); } return translationX - getScaleIconShift(); } /** Loading Loading @@ -976,7 +1039,7 @@ public class BubbleBarView extends FrameLayout { final int index = indexOfChild(mSelectedBubbleView); final float selectedBubbleTranslationX = getExpandedBubbleTranslationX( index, getChildCount(), bubbleBarLocation.isOnLeft(isLayoutRtl())); return getPaddingStart() + selectedBubbleTranslationX + mIconSize / 2f; return selectedBubbleTranslationX + mIconSize / 2f; } private float arrowPositionForSelectedWhenCollapsed(BubbleBarLocation bubbleBarLocation) { Loading @@ -990,7 +1053,7 @@ public class BubbleBarView extends FrameLayout { bubblePosition = index >= MAX_VISIBLE_BUBBLES_COLLAPSED ? MAX_VISIBLE_BUBBLES_COLLAPSED - 1 : index; } return getPaddingStart() + bubblePosition * (mIconOverlapAmount) + mIconSize / 2f; return mBubbleBarPadding + bubblePosition * (mIconOverlapAmount) + getScaledIconSize() / 2f; } @Override Loading Loading @@ -1038,7 +1101,6 @@ public class BubbleBarView extends FrameLayout { */ public float expandedWidth() { final int childCount = getChildCount(); final int horizontalPadding = getPaddingStart() + getPaddingEnd(); // spaces amount is less than child count by 1, or 0 if no child views final float totalSpace; final float totalIconSize; Loading @@ -1047,22 +1109,22 @@ public class BubbleBarView extends FrameLayout { // expanded, so we have at least 2 bubbles in the bubble bar. final float newBubbleScale = mNewBubbleScaleInAnimator.getAnimatedFraction(); totalSpace = (childCount - 2 + newBubbleScale) * mExpandedBarIconsSpacing; totalIconSize = (childCount - 1 + newBubbleScale) * mIconSize; totalIconSize = (childCount - 1 + newBubbleScale) * getScaledIconSize(); } else { totalSpace = Math.max(childCount - 1, 0) * mExpandedBarIconsSpacing; totalIconSize = childCount * mIconSize; totalIconSize = childCount * getScaledIconSize(); } return totalIconSize + totalSpace + horizontalPadding; return totalIconSize + totalSpace + 2 * mBubbleBarPadding; } private float collapsedWidth() { final int childCount = getChildCount(); final int horizontalPadding = getPaddingStart() + getPaddingEnd(); final float horizontalPadding = 2 * mBubbleBarPadding; // If there are more than 2 bubbles, the first 2 should be visible when collapsed. // Otherwise just the first bubble should be visible because we don't show the overflow. return childCount > MAX_VISIBLE_BUBBLES_COLLAPSED ? mIconSize + mIconOverlapAmount + horizontalPadding : mIconSize + horizontalPadding; ? getScaledIconSize() + mIconOverlapAmount + horizontalPadding : getScaledIconSize() + horizontalPadding; } private float getBubbleBarExpandedHeight() { Loading @@ -1071,7 +1133,7 @@ public class BubbleBarView extends FrameLayout { float getBubbleBarCollapsedHeight() { // the pointer is invisible when collapsed return mIconSize + mBubbleBarPadding * 2; return getScaledIconSize() + mBubbleBarPadding * 2; } /** Loading Loading @@ -1103,6 +1165,7 @@ public class BubbleBarView extends FrameLayout { return mIsAnimatingNewBubble; } private boolean hasOverview() { // Overview is always the last bubble View lastChild = getChildAt(getChildCount() - 1); Loading Loading @@ -1144,6 +1207,46 @@ public class BubbleBarView extends FrameLayout { setContentDescription(contentDesc); } private boolean isIconSizeOrPaddingUpdated(float newIconSize, float newBubbleBarPadding) { return isIconSizeUpdated(newIconSize) || isPaddingUpdated(newBubbleBarPadding); } private boolean isIconSizeUpdated(float newIconSize) { return Float.compare(mIconSize, newIconSize) != 0; } private boolean isPaddingUpdated(float newBubbleBarPadding) { return Float.compare(mBubbleBarPadding, newBubbleBarPadding) != 0; } private void addAnimationCallBacks(@NonNull ValueAnimator animator, @Nullable Runnable onStart, @Nullable Runnable onEnd, @Nullable ValueAnimator.AnimatorUpdateListener onUpdate) { if (onUpdate != null) animator.addUpdateListener(onUpdate); animator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationCancel(Animator animator) { } @Override public void onAnimationStart(Animator animator) { if (onStart != null) onStart.run(); } @Override public void onAnimationEnd(Animator animator) { if (onEnd != null) onEnd.run(); } @Override public void onAnimationRepeat(Animator animator) { } }); } /** Interface for BubbleBarView to communicate with its controller. */ interface Controller { Loading quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java +28 −31 Original line number Diff line number Diff line Loading @@ -108,9 +108,11 @@ public class BubbleBarViewController { mBubbleDragController = bubbleControllers.bubbleDragController; mTaskbarStashController = controllers.taskbarStashController; mTaskbarInsetsController = controllers.taskbarInsetsController; mBubbleBarViewAnimator = new BubbleBarViewAnimator(mBarView, mBubbleStashController); mActivity.addOnDeviceProfileChangeListener(dp -> setBubbleBarIconSize(dp.taskbarIconSize)); setBubbleBarIconSize(mActivity.getDeviceProfile().taskbarIconSize); mActivity.addOnDeviceProfileChangeListener( dp -> updateBubbleBarIconSize(dp.taskbarIconSize, /* animate= */ true)); updateBubbleBarIconSize(mActivity.getDeviceProfile().taskbarIconSize, /* animate= */ false); mBubbleBarScale.updateValue(1f); mBubbleClickListener = v -> onBubbleClicked(v); mBubbleBarClickListener = v -> onBubbleBarClicked(); Loading @@ -123,8 +125,6 @@ public class BubbleBarViewController { mBoundsChangeListener.onBoundsChanged(); } }); mBubbleBarViewAnimator = new BubbleBarViewAnimator(mBarView, mBubbleStashController); mBarView.setController(new BubbleBarView.Controller() { @Override public float getBubbleBarTranslationY() { Loading Loading @@ -299,33 +299,6 @@ public class BubbleBarViewController { } } private void setBubbleBarIconSize(int newIconSize) { if (newIconSize == mIconSize) { return; } Resources res = mActivity.getResources(); DisplayMetrics dm = res.getDisplayMetrics(); float smallIconSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, APP_ICON_SMALL_DP, dm); float mediumIconSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, APP_ICON_MEDIUM_DP, dm); float largeIconSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, APP_ICON_LARGE_DP, dm); float smallMediumThreshold = (smallIconSize + mediumIconSize) / 2f; float mediumLargeThreshold = (mediumIconSize + largeIconSize) / 2f; mIconSize = newIconSize <= smallMediumThreshold ? res.getDimensionPixelSize(R.dimen.bubblebar_icon_size_small) : res.getDimensionPixelSize(R.dimen.bubblebar_icon_size); float bubbleBarPadding = newIconSize >= mediumLargeThreshold ? res.getDimensionPixelSize(R.dimen.bubblebar_icon_spacing_large) : res.getDimensionPixelSize(R.dimen.bubblebar_icon_spacing); mBarView.setIconSizeAndPadding(mIconSize, bubbleBarPadding); mBarView.setPadding((int) bubbleBarPadding, mBarView.getPaddingTop(), (int) bubbleBarPadding, mBarView.getPaddingBottom()); } /** Sets a callback that updates the selected bubble after the bubble bar collapses. */ public void setUpdateSelectedBubbleAfterCollapse( Consumer<String> updateSelectedBubbleAfterCollapse) { Loading Loading @@ -360,6 +333,30 @@ public class BubbleBarViewController { // Modifying view related properties. // private void updateBubbleBarIconSize(int newIconSize, boolean animate) { Resources res = mActivity.getResources(); DisplayMetrics dm = res.getDisplayMetrics(); float smallIconSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, APP_ICON_SMALL_DP, dm); float mediumIconSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, APP_ICON_MEDIUM_DP, dm); float largeIconSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, APP_ICON_LARGE_DP, dm); float smallMediumThreshold = (smallIconSize + mediumIconSize) / 2f; float mediumLargeThreshold = (mediumIconSize + largeIconSize) / 2f; mIconSize = newIconSize <= smallMediumThreshold ? res.getDimensionPixelSize(R.dimen.bubblebar_icon_size_small) : res.getDimensionPixelSize(R.dimen.bubblebar_icon_size); float bubbleBarPadding = newIconSize >= mediumLargeThreshold ? res.getDimensionPixelSize(R.dimen.bubblebar_icon_spacing_large) : res.getDimensionPixelSize(R.dimen.bubblebar_icon_spacing); if (animate) { mBarView.animateBubbleBarIconSize(mIconSize, bubbleBarPadding); } else { mBarView.setIconSizeAndPadding(mIconSize, bubbleBarPadding); } } /** * Sets the translation of the bubble bar during the swipe up gesture. */ Loading Loading
quickstep/res/layout/transient_taskbar.xml +0 −2 Original line number Diff line number Diff line Loading @@ -45,8 +45,6 @@ android:layout_gravity="bottom|end" android:layout_marginHorizontal="@dimen/transient_taskbar_bottom_margin" android:paddingTop="@dimen/bubblebar_pointer_visible_size" android:paddingEnd="@dimen/taskbar_icon_spacing" android:paddingStart="@dimen/taskbar_icon_spacing" android:visibility="gone" android:gravity="center" android:clipChildren="false" Loading
quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt +7 −6 Original line number Diff line number Diff line Loading @@ -123,22 +123,23 @@ class BubbleBarBackground(context: Context, private var backgroundHeight: Float) ) // Create background path val backgroundPath = Path() val topOffset = backgroundHeight - bounds.height().toFloat() val radius = backgroundHeight / 2f val left = bounds.left + (if (anchorLeft) 0f else bounds.width().toFloat() - width) val right = bounds.left + (if (anchorLeft) width else bounds.width().toFloat()) val top = bounds.top + arrowVisibleHeight val top = bounds.top - topOffset + arrowVisibleHeight val bottom = bounds.top + bounds.height().toFloat() backgroundPath.addRoundRect(left, top, right, bottom, radius, radius, Path.Direction.CW) addArrowPathIfNeeded(backgroundPath) addArrowPathIfNeeded(backgroundPath, topOffset) // Draw background. canvas.drawPath(backgroundPath, fillPaint) canvas.drawPath(backgroundPath, strokePaint) canvas.restore() } private fun addArrowPathIfNeeded(sourcePath: Path) { private fun addArrowPathIfNeeded(sourcePath: Path, topOffset: Float) { if (!showingArrow || arrowHeightFraction <= 0) return val arrowPath = Path() RoundedArrowDrawable.addDownPointingRoundedTriangleToPath( Loading @@ -153,7 +154,7 @@ class BubbleBarBackground(context: Context, private var backgroundHeight: Float) arrowPath.transform(pathTransform) // shift to arrow position val arrowStart = bounds.left + arrowPositionX - (arrowWidth / 2f) val arrowTop = (1 - arrowHeightFraction) * arrowVisibleHeight val arrowTop = (1 - arrowHeightFraction) * arrowVisibleHeight - topOffset arrowPath.offset(arrowStart, arrowTop) // union with rectangle sourcePath.op(arrowPath, Path.Op.UNION) Loading @@ -180,7 +181,7 @@ class BubbleBarBackground(context: Context, private var backgroundHeight: Float) fillPaint.colorFilter = colorFilter } fun setHeight(newHeight: Float) { fun setBackgroundHeight(newHeight: Float) { backgroundHeight = newHeight } Loading
quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java +169 −66 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.content.Context; Loading @@ -44,6 +45,7 @@ import androidx.dynamicanimation.animation.SpringForce; import com.android.launcher3.R; import com.android.launcher3.anim.SpringAnimationBuilder; import com.android.launcher3.util.DisplayController; import com.android.wm.shell.Flags; import com.android.wm.shell.common.bubbles.BubbleBarLocation; import java.util.List; Loading Loading @@ -85,6 +87,7 @@ public class BubbleBarView extends FrameLayout { private static final int MAX_VISIBLE_BUBBLES_COLLAPSED = 2; private static final int ARROW_POSITION_ANIMATION_DURATION_MS = 200; private static final int WIDTH_ANIMATION_DURATION_MS = 200; private static final int SCALE_ANIMATION_DURATION_MS = 200; private static final long FADE_OUT_ANIM_ALPHA_DURATION_MS = 50L; private static final long FADE_OUT_ANIM_ALPHA_DELAY_MS = 50L; Loading Loading @@ -135,13 +138,12 @@ public class BubbleBarView extends FrameLayout { private float mBubbleBarPadding; // The size of a bubble in the bar private float mIconSize; // The scale of bubble icons private float mIconScale = 1f; // The elevation of the bubbles within the bar private final float mBubbleElevation; private final float mDragElevation; private final int mPointerSize; private final Rect mTempBackgroundBounds = new Rect(); // Whether the bar is expanded (i.e. the bubble activity is being displayed). private boolean mIsBarExpanded = false; // The currently selected bubble view. Loading @@ -162,7 +164,8 @@ public class BubbleBarView extends FrameLayout { /** An animator used for scaling in a new bubble to the bubble bar while expanded. */ @Nullable private ValueAnimator mNewBubbleScaleInAnimator = null; @Nullable private ValueAnimator mScalePaddingAnimator; @Nullable private Animator mBubbleBarLocationAnimator = null; Loading Loading @@ -217,17 +220,10 @@ public class BubbleBarView extends FrameLayout { setBackgroundDrawable(mBubbleBarBackground); mWidthAnimator.setDuration(WIDTH_ANIMATION_DURATION_MS); mWidthAnimator.addUpdateListener(animation -> { updateBubblesLayoutProperties(mBubbleBarLocation); invalidate(); }); mWidthAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { addAnimationCallBacks(mWidthAnimator, /* onStart= */ () -> mBubbleBarBackground.showArrow(true), /* onEnd= */ () -> { mBubbleBarBackground.showArrow(mIsBarExpanded); if (!mIsBarExpanded && mReorderRunnable != null) { mReorderRunnable.run(); Loading @@ -242,17 +238,56 @@ public class BubbleBarView extends FrameLayout { mUpdateSelectedBubbleAfterCollapse.accept(firstBubble.getBubble().getKey()); } updateWidth(); }, /* onUpdate= */ animator -> { updateBubblesLayoutProperties(mBubbleBarLocation); invalidate(); }); } @Override public void onAnimationRepeat(Animator animation) { } @Override public void onAnimationStart(Animator animation) { mBubbleBarBackground.showArrow(true); /** * Animates icon sizes and spacing between icons and bubble bar borders. * * @param newIconSize new icon size * @param newBubbleBarPadding spacing between icons and bubble bar borders. */ public void animateBubbleBarIconSize(float newIconSize, float newBubbleBarPadding) { if (!isIconSizeOrPaddingUpdated(newIconSize, newBubbleBarPadding)) { return; } if (!Flags.animateBubbleSizeChange()) { setIconSizeAndPadding(newIconSize, newBubbleBarPadding); } if (mScalePaddingAnimator != null && mScalePaddingAnimator.isRunning()) { mScalePaddingAnimator.cancel(); } ValueAnimator scalePaddingAnimator = ValueAnimator.ofFloat(0f, 1f); scalePaddingAnimator.setDuration(SCALE_ANIMATION_DURATION_MS); boolean isPaddingUpdated = isPaddingUpdated(newBubbleBarPadding); boolean isIconSizeUpdated = isIconSizeUpdated(newIconSize); float initialScale = mIconScale; float initialPadding = mBubbleBarPadding; float targetScale = newIconSize / getScaledIconSize(); addAnimationCallBacks(scalePaddingAnimator, /* onStart= */ null, /* onEnd= */ () -> setIconSizeAndPadding(newIconSize, newBubbleBarPadding), /* onUpdate= */ animator -> { float transitionProgress = (float) animator.getAnimatedValue(); if (isIconSizeUpdated) { mIconScale = initialScale + (targetScale - initialScale) * transitionProgress; } if (isPaddingUpdated) { mBubbleBarPadding = initialPadding + (newBubbleBarPadding - initialPadding) * transitionProgress; } updateBubblesLayoutProperties(mBubbleBarLocation); invalidate(); }); scalePaddingAnimator.start(); mScalePaddingAnimator = scalePaddingAnimator; } @Override Loading @@ -267,28 +302,37 @@ public class BubbleBarView extends FrameLayout { } /** * Sets new icon size and spacing between icons and bubble bar borders. * Sets new icon sizes and newBubbleBarPadding between icons and bubble bar borders. * * @param newIconSize new icon size * @param spacing spacing between icons and bubble bar borders. * @param newBubbleBarPadding newBubbleBarPadding between icons and bubble bar borders. */ // TODO(b/335575529): animate bubble bar icons size change public void setIconSizeAndPadding(float newIconSize, float spacing) { public void setIconSizeAndPadding(float newIconSize, float newBubbleBarPadding) { // TODO(b/335457839): handle new bubble animation during the size change mBubbleBarPadding = spacing; if (!isIconSizeOrPaddingUpdated(newIconSize, newBubbleBarPadding)) { return; } mIconScale = 1f; mBubbleBarPadding = newBubbleBarPadding; mIconSize = newIconSize; int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View childView = getChildAt(i); childView.setScaleY(mIconScale); childView.setScaleY(mIconScale); FrameLayout.LayoutParams params = (LayoutParams) childView.getLayoutParams(); params.height = (int) mIconSize; params.width = (int) mIconSize; childView.setLayoutParams(params); } mBubbleBarBackground.setHeight(getBubbleBarExpandedHeight()); mBubbleBarBackground.setBackgroundHeight(getBubbleBarHeight()); updateLayoutParams(); } private float getScaledIconSize() { return mIconSize * mIconScale; } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); Loading Loading @@ -698,6 +742,11 @@ public class BubbleBarView extends FrameLayout { setLayoutParams(lp); } private float getBubbleBarHeight() { return mIsBarExpanded ? getBubbleBarExpandedHeight() : getBubbleBarCollapsedHeight(); } /** @return the horizontal margin between the bubble bar and the edge of the screen. */ int getHorizontalMargin() { LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams(); Loading @@ -714,7 +763,10 @@ public class BubbleBarView extends FrameLayout { final float expandedWidth = expandedWidth(); final float collapsedWidth = collapsedWidth(); int bubbleCount = getChildCount(); final float ty = (mBubbleBarBounds.height() - mIconSize) / 2f; float viewBottom = mBubbleBarBounds.height() + (isExpanded() ? mPointerSize : 0); float bubbleBarAnimatedTop = viewBottom - getBubbleBarHeight(); // When translating X & Y the scale is ignored, so need to deduct it from the translations final float ty = bubbleBarAnimatedTop + mBubbleBarPadding - getScaleIconShift(); final boolean animate = getVisibility() == VISIBLE; final boolean onLeft = bubbleBarLocation.isOnLeft(isLayoutRtl()); // elevation state is opposite to widthState - when expanded all icons are flat Loading @@ -729,8 +781,9 @@ public class BubbleBarView extends FrameLayout { bv.setDragTranslationX(0f); bv.setOffsetX(0f); bv.setScaleX(mIconScale); bv.setScaleY(mIconScale); bv.setTranslationY(ty); // the position of the bubble when the bar is fully expanded final float expandedX = getExpandedBubbleTranslationX(i, bubbleCount, onLeft); // the position of the bubble when the bar is fully collapsed Loading Loading @@ -795,21 +848,28 @@ public class BubbleBarView extends FrameLayout { mBubbleBarBackground.setArrowPosition(arrowPosition); mBubbleBarBackground.setArrowHeightFraction(widthState); mBubbleBarBackground.setWidth(interpolatedWidth); mBubbleBarBackground.setBackgroundHeight(getBubbleBarExpandedHeight()); } private float getScaleIconShift() { return (mIconSize - getScaledIconSize()) / 2; } private float getExpandedBubbleTranslationX(int bubbleIndex, int bubbleCount, boolean onLeft) { if (bubbleIndex < 0 || bubbleIndex >= bubbleCount) { return 0; } final float iconAndSpacing = mIconSize + mExpandedBarIconsSpacing; final float iconAndSpacing = getScaledIconSize() + mExpandedBarIconsSpacing; float translationX; if (mNewBubbleScaleInAnimator != null && mNewBubbleScaleInAnimator.isRunning()) { return getExpandedBubbleTranslationXDuringScaleAnimation( translationX = getExpandedBubbleTranslationXDuringScaleAnimation( bubbleIndex, bubbleCount, onLeft); } else if (onLeft) { return (bubbleCount - bubbleIndex - 1) * iconAndSpacing; translationX = mBubbleBarPadding + (bubbleCount - bubbleIndex - 1) * iconAndSpacing; } else { return bubbleIndex * iconAndSpacing; translationX = mBubbleBarPadding + bubbleIndex * iconAndSpacing; } return translationX - getScaleIconShift(); } /** Loading @@ -830,17 +890,17 @@ public class BubbleBarView extends FrameLayout { // compiler doesn't know that. return 0; } final float iconAndSpacing = mIconSize + mExpandedBarIconsSpacing; final float iconAndSpacing = getScaledIconSize() + mExpandedBarIconsSpacing; final float newBubbleScale = mNewBubbleScaleInAnimator.getAnimatedFraction(); // the new bubble is scaling in from the center, so we need to adjust its translation so // that the distance to the adjacent bubble scales at the same rate. final float pivotAdjustment = -(1 - newBubbleScale) * mIconSize / 2f; final float pivotAdjustment = -(1 - newBubbleScale) * getScaledIconSize() / 2f; if (onLeft) { if (bubbleIndex == 0) { // this is the animating bubble. use scaled spacing between it and the bubble to // its left return (bubbleCount - 1) * mIconSize return (bubbleCount - 1) * getScaledIconSize() + (bubbleCount - 2) * mExpandedBarIconsSpacing + newBubbleScale * mExpandedBarIconsSpacing + pivotAdjustment; Loading @@ -862,13 +922,16 @@ public class BubbleBarView extends FrameLayout { if (bubbleIndex < 0 || bubbleIndex >= bubbleCount) { return 0; } float translationX; if (onLeft) { // Shift the first bubble only if there are more bubbles in addition to overflow return bubbleIndex == 0 && bubbleCount > MAX_VISIBLE_BUBBLES_COLLAPSED ? mIconOverlapAmount : 0; translationX = mBubbleBarPadding + ( bubbleIndex == 0 && bubbleCount > MAX_VISIBLE_BUBBLES_COLLAPSED ? mIconOverlapAmount : 0); } else { return bubbleIndex == 0 ? 0 : mIconOverlapAmount; translationX = mBubbleBarPadding + (bubbleIndex == 0 ? 0 : mIconOverlapAmount); } return translationX - getScaleIconShift(); } /** Loading Loading @@ -976,7 +1039,7 @@ public class BubbleBarView extends FrameLayout { final int index = indexOfChild(mSelectedBubbleView); final float selectedBubbleTranslationX = getExpandedBubbleTranslationX( index, getChildCount(), bubbleBarLocation.isOnLeft(isLayoutRtl())); return getPaddingStart() + selectedBubbleTranslationX + mIconSize / 2f; return selectedBubbleTranslationX + mIconSize / 2f; } private float arrowPositionForSelectedWhenCollapsed(BubbleBarLocation bubbleBarLocation) { Loading @@ -990,7 +1053,7 @@ public class BubbleBarView extends FrameLayout { bubblePosition = index >= MAX_VISIBLE_BUBBLES_COLLAPSED ? MAX_VISIBLE_BUBBLES_COLLAPSED - 1 : index; } return getPaddingStart() + bubblePosition * (mIconOverlapAmount) + mIconSize / 2f; return mBubbleBarPadding + bubblePosition * (mIconOverlapAmount) + getScaledIconSize() / 2f; } @Override Loading Loading @@ -1038,7 +1101,6 @@ public class BubbleBarView extends FrameLayout { */ public float expandedWidth() { final int childCount = getChildCount(); final int horizontalPadding = getPaddingStart() + getPaddingEnd(); // spaces amount is less than child count by 1, or 0 if no child views final float totalSpace; final float totalIconSize; Loading @@ -1047,22 +1109,22 @@ public class BubbleBarView extends FrameLayout { // expanded, so we have at least 2 bubbles in the bubble bar. final float newBubbleScale = mNewBubbleScaleInAnimator.getAnimatedFraction(); totalSpace = (childCount - 2 + newBubbleScale) * mExpandedBarIconsSpacing; totalIconSize = (childCount - 1 + newBubbleScale) * mIconSize; totalIconSize = (childCount - 1 + newBubbleScale) * getScaledIconSize(); } else { totalSpace = Math.max(childCount - 1, 0) * mExpandedBarIconsSpacing; totalIconSize = childCount * mIconSize; totalIconSize = childCount * getScaledIconSize(); } return totalIconSize + totalSpace + horizontalPadding; return totalIconSize + totalSpace + 2 * mBubbleBarPadding; } private float collapsedWidth() { final int childCount = getChildCount(); final int horizontalPadding = getPaddingStart() + getPaddingEnd(); final float horizontalPadding = 2 * mBubbleBarPadding; // If there are more than 2 bubbles, the first 2 should be visible when collapsed. // Otherwise just the first bubble should be visible because we don't show the overflow. return childCount > MAX_VISIBLE_BUBBLES_COLLAPSED ? mIconSize + mIconOverlapAmount + horizontalPadding : mIconSize + horizontalPadding; ? getScaledIconSize() + mIconOverlapAmount + horizontalPadding : getScaledIconSize() + horizontalPadding; } private float getBubbleBarExpandedHeight() { Loading @@ -1071,7 +1133,7 @@ public class BubbleBarView extends FrameLayout { float getBubbleBarCollapsedHeight() { // the pointer is invisible when collapsed return mIconSize + mBubbleBarPadding * 2; return getScaledIconSize() + mBubbleBarPadding * 2; } /** Loading Loading @@ -1103,6 +1165,7 @@ public class BubbleBarView extends FrameLayout { return mIsAnimatingNewBubble; } private boolean hasOverview() { // Overview is always the last bubble View lastChild = getChildAt(getChildCount() - 1); Loading Loading @@ -1144,6 +1207,46 @@ public class BubbleBarView extends FrameLayout { setContentDescription(contentDesc); } private boolean isIconSizeOrPaddingUpdated(float newIconSize, float newBubbleBarPadding) { return isIconSizeUpdated(newIconSize) || isPaddingUpdated(newBubbleBarPadding); } private boolean isIconSizeUpdated(float newIconSize) { return Float.compare(mIconSize, newIconSize) != 0; } private boolean isPaddingUpdated(float newBubbleBarPadding) { return Float.compare(mBubbleBarPadding, newBubbleBarPadding) != 0; } private void addAnimationCallBacks(@NonNull ValueAnimator animator, @Nullable Runnable onStart, @Nullable Runnable onEnd, @Nullable ValueAnimator.AnimatorUpdateListener onUpdate) { if (onUpdate != null) animator.addUpdateListener(onUpdate); animator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationCancel(Animator animator) { } @Override public void onAnimationStart(Animator animator) { if (onStart != null) onStart.run(); } @Override public void onAnimationEnd(Animator animator) { if (onEnd != null) onEnd.run(); } @Override public void onAnimationRepeat(Animator animator) { } }); } /** Interface for BubbleBarView to communicate with its controller. */ interface Controller { Loading
quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java +28 −31 Original line number Diff line number Diff line Loading @@ -108,9 +108,11 @@ public class BubbleBarViewController { mBubbleDragController = bubbleControllers.bubbleDragController; mTaskbarStashController = controllers.taskbarStashController; mTaskbarInsetsController = controllers.taskbarInsetsController; mBubbleBarViewAnimator = new BubbleBarViewAnimator(mBarView, mBubbleStashController); mActivity.addOnDeviceProfileChangeListener(dp -> setBubbleBarIconSize(dp.taskbarIconSize)); setBubbleBarIconSize(mActivity.getDeviceProfile().taskbarIconSize); mActivity.addOnDeviceProfileChangeListener( dp -> updateBubbleBarIconSize(dp.taskbarIconSize, /* animate= */ true)); updateBubbleBarIconSize(mActivity.getDeviceProfile().taskbarIconSize, /* animate= */ false); mBubbleBarScale.updateValue(1f); mBubbleClickListener = v -> onBubbleClicked(v); mBubbleBarClickListener = v -> onBubbleBarClicked(); Loading @@ -123,8 +125,6 @@ public class BubbleBarViewController { mBoundsChangeListener.onBoundsChanged(); } }); mBubbleBarViewAnimator = new BubbleBarViewAnimator(mBarView, mBubbleStashController); mBarView.setController(new BubbleBarView.Controller() { @Override public float getBubbleBarTranslationY() { Loading Loading @@ -299,33 +299,6 @@ public class BubbleBarViewController { } } private void setBubbleBarIconSize(int newIconSize) { if (newIconSize == mIconSize) { return; } Resources res = mActivity.getResources(); DisplayMetrics dm = res.getDisplayMetrics(); float smallIconSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, APP_ICON_SMALL_DP, dm); float mediumIconSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, APP_ICON_MEDIUM_DP, dm); float largeIconSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, APP_ICON_LARGE_DP, dm); float smallMediumThreshold = (smallIconSize + mediumIconSize) / 2f; float mediumLargeThreshold = (mediumIconSize + largeIconSize) / 2f; mIconSize = newIconSize <= smallMediumThreshold ? res.getDimensionPixelSize(R.dimen.bubblebar_icon_size_small) : res.getDimensionPixelSize(R.dimen.bubblebar_icon_size); float bubbleBarPadding = newIconSize >= mediumLargeThreshold ? res.getDimensionPixelSize(R.dimen.bubblebar_icon_spacing_large) : res.getDimensionPixelSize(R.dimen.bubblebar_icon_spacing); mBarView.setIconSizeAndPadding(mIconSize, bubbleBarPadding); mBarView.setPadding((int) bubbleBarPadding, mBarView.getPaddingTop(), (int) bubbleBarPadding, mBarView.getPaddingBottom()); } /** Sets a callback that updates the selected bubble after the bubble bar collapses. */ public void setUpdateSelectedBubbleAfterCollapse( Consumer<String> updateSelectedBubbleAfterCollapse) { Loading Loading @@ -360,6 +333,30 @@ public class BubbleBarViewController { // Modifying view related properties. // private void updateBubbleBarIconSize(int newIconSize, boolean animate) { Resources res = mActivity.getResources(); DisplayMetrics dm = res.getDisplayMetrics(); float smallIconSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, APP_ICON_SMALL_DP, dm); float mediumIconSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, APP_ICON_MEDIUM_DP, dm); float largeIconSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, APP_ICON_LARGE_DP, dm); float smallMediumThreshold = (smallIconSize + mediumIconSize) / 2f; float mediumLargeThreshold = (mediumIconSize + largeIconSize) / 2f; mIconSize = newIconSize <= smallMediumThreshold ? res.getDimensionPixelSize(R.dimen.bubblebar_icon_size_small) : res.getDimensionPixelSize(R.dimen.bubblebar_icon_size); float bubbleBarPadding = newIconSize >= mediumLargeThreshold ? res.getDimensionPixelSize(R.dimen.bubblebar_icon_spacing_large) : res.getDimensionPixelSize(R.dimen.bubblebar_icon_spacing); if (animate) { mBarView.animateBubbleBarIconSize(mIconSize, bubbleBarPadding); } else { mBarView.setIconSizeAndPadding(mIconSize, bubbleBarPadding); } } /** * Sets the translation of the bubble bar during the swipe up gesture. */ Loading