Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 60101ae7 authored by Ats Jenk's avatar Ats Jenk Committed by Android (Google) Code Review
Browse files

Merge changes from topic "bubble-bar-drag" into main

* changes:
  Fix bubble bar arrow visibility during animation
  Animate bubble bar location changes
  Introduce bubble bar location that is driven by shell
parents 9d083806 4186cf1b
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -41,9 +41,10 @@
    <com.android.launcher3.taskbar.bubbles.BubbleBarView
        android:id="@+id/taskbar_bubbles"
        android:layout_width="wrap_content"
        android:layout_height="@dimen/bubblebar_size"
        android:layout_height="@dimen/bubblebar_size_with_pointer"
        android:layout_gravity="bottom|end"
        android:layout_marginEnd="@dimen/transient_taskbar_bottom_margin"
        android:layout_marginHorizontal="@dimen/transient_taskbar_bottom_margin"
        android:paddingTop="@dimen/bubblebar_pointer_size"
        android:paddingEnd="@dimen/taskbar_icon_spacing"
        android:paddingStart="@dimen/taskbar_icon_spacing"
        android:visibility="gone"
+2 −0
Original line number Diff line number Diff line
@@ -413,6 +413,8 @@
    <dimen name="bubblebar_stashed_size">@dimen/transient_taskbar_stashed_height</dimen>
    <dimen name="bubblebar_stashed_handle_height">@dimen/taskbar_stashed_handle_height</dimen>
    <dimen name="bubblebar_pointer_size">8dp</dimen>
    <!-- Container size with pointer included: bubblebar_size + bubblebar_pointer_size -->
    <dimen name="bubblebar_size_with_pointer">80dp</dimen>
    <dimen name="bubblebar_elevation">1dp</dimen>
    <dimen name="bubblebar_hotseat_adjustment_threshold">90dp</dimen>

+23 −5
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ import android.graphics.Canvas
import android.graphics.Color
import android.graphics.ColorFilter
import android.graphics.Paint
import android.graphics.PixelFormat
import android.graphics.drawable.Drawable
import android.graphics.drawable.ShapeDrawable
import com.android.app.animation.Interpolators
@@ -122,14 +123,22 @@ class BubbleBarBackground(context: TaskbarActivityContext, private val backgroun

        // Draw background.
        val radius = backgroundHeight / 2f
        val left = if (anchorLeft) 0f else canvas.width.toFloat() - width
        val right = if (anchorLeft) width else canvas.width.toFloat()
        canvas.drawRoundRect(left, 0f, right, canvas.height.toFloat(), radius, radius, paint)
        val left = if (anchorLeft) 0f else bounds.width().toFloat() - width
        val right = if (anchorLeft) width else bounds.width().toFloat()
        canvas.drawRoundRect(
            left,
            pointerSize,
            right,
            bounds.height().toFloat(),
            radius,
            radius,
            paint
        )

        if (showingArrow) {
            // Draw arrow.
            val transX = arrowPositionX - pointerSize / 2f
            canvas.translate(transX, -pointerSize)
            canvas.translate(transX, 0f)
            arrowDrawable.draw(canvas)
        }

@@ -137,11 +146,20 @@ class BubbleBarBackground(context: TaskbarActivityContext, private val backgroun
    }

    override fun getOpacity(): Int {
        return paint.alpha
        return when (paint.alpha) {
            255 -> PixelFormat.OPAQUE
            0 -> PixelFormat.TRANSPARENT
            else -> PixelFormat.TRANSLUCENT
        }
    }

    override fun setAlpha(alpha: Int) {
        paint.alpha = alpha
        arrowDrawable.paint.alpha = alpha
    }

    override fun getAlpha(): Int {
        return paint.alpha
    }

    override fun setColorFilter(colorFilter: ColorFilter?) {
+15 −2
Original line number Diff line number Diff line
@@ -73,6 +73,7 @@ import com.android.launcher3.util.Executors.SimpleThreadFactory;
import com.android.quickstep.SystemUiProxy;
import com.android.wm.shell.Flags;
import com.android.wm.shell.bubbles.IBubblesListener;
import com.android.wm.shell.common.bubbles.BubbleBarLocation;
import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
import com.android.wm.shell.common.bubbles.BubbleInfo;
import com.android.wm.shell.common.bubbles.RemovedBubble;
@@ -155,12 +156,14 @@ public class BubbleBarController extends IBubblesListener.Stub {
     * {@link BubbleBarBubble}s so that it can be used to update the views.
     */
    private static class BubbleBarViewUpdate {
        final boolean initialState;
        boolean expandedChanged;
        boolean expanded;
        boolean shouldShowEducation;
        String selectedBubbleKey;
        String suppressedBubbleKey;
        String unsuppressedBubbleKey;
        BubbleBarLocation bubbleBarLocation;
        List<RemovedBubble> removedBubbles;
        List<String> bubbleKeysInOrder;

@@ -170,12 +173,14 @@ public class BubbleBarController extends IBubblesListener.Stub {
        List<BubbleBarBubble> currentBubbles;

        BubbleBarViewUpdate(BubbleBarUpdate update) {
            initialState = update.initialState;
            expandedChanged = update.expandedChanged;
            expanded = update.expanded;
            shouldShowEducation = update.shouldShowEducation;
            selectedBubbleKey = update.selectedBubbleKey;
            suppressedBubbleKey = update.suppressedBubbleKey;
            unsuppressedBubbleKey = update.unsupressedBubbleKey;
            bubbleBarLocation = update.bubbleBarLocation;
            removedBubbles = update.removedBubbles;
            bubbleKeysInOrder = update.bubbleKeysInOrder;
        }
@@ -400,6 +405,14 @@ public class BubbleBarController extends IBubblesListener.Stub {
                Log.w(TAG, "expansion was changed but is the same");
            }
        }
        if (update.bubbleBarLocation != null) {
            if (update.bubbleBarLocation != mBubbleBarViewController.getBubbleBarLocation()) {
                // Animate when receiving updates. Skip it if we received the initial state.
                boolean animate = !update.initialState;
                mBubbleBarViewController.setBubbleBarLocation(update.bubbleBarLocation, animate);
                mBubbleStashController.setBubbleBarLocation(update.bubbleBarLocation);
            }
        }
    }

    /** Tells WMShell to show the currently selected bubble. */
@@ -593,7 +606,7 @@ public class BubbleBarController extends IBubblesListener.Stub {
        Rect location = new Rect();
        // currentBarBounds is only useful for distance from left or right edge.
        // It contains the current bounds, calculate the expanded bounds.
        if (mBarView.isOnLeft()) {
        if (mBarView.getBubbleBarLocation().isOnLeft(mBarView.isLayoutRtl())) {
            location.left = currentBarBounds.left;
            location.right = (int) (currentBarBounds.left + mBarView.expandedWidth());
        } else {
@@ -601,7 +614,7 @@ public class BubbleBarController extends IBubblesListener.Stub {
            location.right = currentBarBounds.right;
        }
        final int translation = (int) abs(mBubbleStashController.getBubbleBarTranslationY());
        location.top = displaySize.y - mBarView.getHeight() - translation;
        location.top = displaySize.y - currentBarBounds.height() - translation;
        location.bottom = displaySize.y - translation;
        return location;
    }
+142 −10
Original line number Diff line number Diff line
@@ -15,21 +15,33 @@
 */
package com.android.launcher3.taskbar.bubbles;

import static com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.LayoutDirection;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;

import androidx.dynamicanimation.animation.SpringForce;

import com.android.launcher3.R;
import com.android.launcher3.anim.SpringAnimationBuilder;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.views.ActivityContext;
import com.android.wm.shell.common.bubbles.BubbleBarLocation;

import java.util.List;
import java.util.function.Consumer;
@@ -70,6 +82,18 @@ public class BubbleBarView extends FrameLayout {
    private static final int ARROW_POSITION_ANIMATION_DURATION_MS = 200;
    private static final int WIDTH_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;
    private static final long FADE_OUT_ANIM_POSITION_DURATION_MS = 100L;
    // During fade out animation we shift the bubble bar 1/80th of the screen width
    private static final float FADE_OUT_ANIM_POSITION_SHIFT = 1 / 80f;

    private static final long FADE_IN_ANIM_ALPHA_DURATION_MS = 100L;
    // Use STIFFNESS_MEDIUMLOW which is not defined in the API constants
    private static final float FADE_IN_ANIM_POSITION_SPRING_STIFFNESS = 400f;
    // During fade in animation we shift the bubble bar 1/60th of the screen width
    private static final float FADE_IN_ANIM_POSITION_SHIFT = 1 / 60f;

    private final BubbleBarBackground mBubbleBarBackground;

    /**
@@ -86,11 +110,13 @@ public class BubbleBarView extends FrameLayout {
    private final float mIconSize;
    // The elevation of the bubbles within the bar
    private final float mBubbleElevation;
    private final int mPointerSize;

    // Whether the bar is expanded (i.e. the bubble activity is being displayed).
    private boolean mIsBarExpanded = false;
    // The currently selected bubble view.
    private BubbleView mSelectedBubbleView;
    private BubbleBarLocation mBubbleBarLocation = BubbleBarLocation.DEFAULT;
    // The click listener when the bubble bar is collapsed.
    private View.OnClickListener mOnClickListener;

@@ -102,6 +128,9 @@ public class BubbleBarView extends FrameLayout {
    // collapsed state and 1 to the fully expanded state.
    private final ValueAnimator mWidthAnimator = ValueAnimator.ofFloat(0, 1);

    @Nullable
    private Animator mBubbleBarLocationAnimator = null;

    // We don't reorder the bubbles when they are expanded as it could be jarring for the user
    // this runnable will be populated with any reordering of the bubbles that should be applied
    // once they are collapsed.
@@ -114,6 +143,8 @@ public class BubbleBarView extends FrameLayout {
    @Nullable
    private BubbleView mDraggedBubbleView;

    private int mPreviousLayoutDirection = LayoutDirection.UNDEFINED;

    public BubbleBarView(Context context) {
        this(context, null);
    }
@@ -136,6 +167,8 @@ public class BubbleBarView extends FrameLayout {
        mIconSpacing = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_spacing);
        mIconSize = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_size);
        mBubbleElevation = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_elevation);
        mPointerSize = getResources().getDimensionPixelSize(R.dimen.bubblebar_pointer_size);

        setClipToPadding(false);

        mBubbleBarBackground = new BubbleBarBackground(activityContext,
@@ -184,7 +217,7 @@ public class BubbleBarView extends FrameLayout {
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        mBubbleBarBounds.left = left;
        mBubbleBarBounds.top = top;
        mBubbleBarBounds.top = top + mPointerSize;
        mBubbleBarBounds.right = right;
        mBubbleBarBounds.bottom = bottom;

@@ -199,24 +232,123 @@ public class BubbleBarView extends FrameLayout {

    @Override
    public void onRtlPropertiesChanged(int layoutDirection) {
        // TODO(b/313661121): set this based on bubble bar position and not LTR or RTL
        boolean onLeft = layoutDirection == LAYOUT_DIRECTION_RTL;
        if (mBubbleBarLocation == BubbleBarLocation.DEFAULT
                && mPreviousLayoutDirection != layoutDirection) {
            Log.d(TAG, "BubbleBar RTL properties changed, new layoutDirection=" + layoutDirection
                    + " previous layoutDirection=" + mPreviousLayoutDirection);
            mPreviousLayoutDirection = layoutDirection;
            onBubbleBarLocationChanged();
        }
    }

    private void onBubbleBarLocationChanged() {
        final boolean onLeft = mBubbleBarLocation.isOnLeft(isLayoutRtl());
        mBubbleBarBackground.setAnchorLeft(onLeft);
        mRelativePivotX = onLeft ? 0f : 1f;
        ViewGroup.LayoutParams layoutParams = getLayoutParams();
        if (layoutParams instanceof LayoutParams lp) {
            lp.gravity = Gravity.BOTTOM | (onLeft ? Gravity.LEFT : Gravity.RIGHT);
            setLayoutParams(lp);
        }
        invalidate();
    }

    /**
     * @return <code>true</code> when bar is pinned to the left edge of the screen
     * @return current {@link BubbleBarLocation}
     */
    public boolean isOnLeft() {
        return getLayoutDirection() == LAYOUT_DIRECTION_RTL;
    public BubbleBarLocation getBubbleBarLocation() {
        return mBubbleBarLocation;
    }

    /**
     * Update {@link BubbleBarLocation}
     */
    public void setBubbleBarLocation(BubbleBarLocation bubbleBarLocation, boolean animate) {
        if (animate) {
            animateToBubbleBarLocation(bubbleBarLocation);
        } else {
            setBubbleBarLocationInternal(bubbleBarLocation);
        }
    }

    private void setBubbleBarLocationInternal(BubbleBarLocation bubbleBarLocation) {
        if (bubbleBarLocation != mBubbleBarLocation) {
            mBubbleBarLocation = bubbleBarLocation;
            onBubbleBarLocationChanged();
            invalidate();
        }
    }

    private void animateToBubbleBarLocation(BubbleBarLocation bubbleBarLocation) {
        if (bubbleBarLocation == mBubbleBarLocation) {
            // nothing to do, already at expected location
            return;
        }
        if (mBubbleBarLocationAnimator != null && mBubbleBarLocationAnimator.isRunning()) {
            mBubbleBarLocationAnimator.cancel();
        }

        // Location animation uses two separate animators.
        // First animator hides the bar.
        // After it completes, location update is sent to layout the bar in the new location.
        // Second animator is started to show the bar.
        mBubbleBarLocationAnimator = getLocationUpdateFadeOutAnimator();
        mBubbleBarLocationAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                // Bubble bar is not visible, update the location
                setBubbleBarLocationInternal(bubbleBarLocation);
                // Animate it in
                mBubbleBarLocationAnimator = getLocationUpdateFadeInAnimator();
                mBubbleBarLocationAnimator.start();
            }
        });
        mBubbleBarLocationAnimator.start();
    }

    private AnimatorSet getLocationUpdateFadeOutAnimator() {
        final float shift =
                getResources().getDisplayMetrics().widthPixels * FADE_OUT_ANIM_POSITION_SHIFT;
        final float tx = mBubbleBarLocation.isOnLeft(isLayoutRtl()) ? shift : -shift;

        ObjectAnimator positionAnim = ObjectAnimator.ofFloat(this, TRANSLATION_X, tx)
                .setDuration(FADE_OUT_ANIM_POSITION_DURATION_MS);
        positionAnim.setInterpolator(EMPHASIZED_ACCELERATE);

        ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(this, ALPHA, 0f)
                .setDuration(FADE_OUT_ANIM_ALPHA_DURATION_MS);
        alphaAnim.setStartDelay(FADE_OUT_ANIM_ALPHA_DELAY_MS);

        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playTogether(positionAnim, alphaAnim);
        return animatorSet;
    }

    private Animator getLocationUpdateFadeInAnimator() {
        final float shift =
                getResources().getDisplayMetrics().widthPixels * FADE_IN_ANIM_POSITION_SHIFT;
        final float startTx = mBubbleBarLocation.isOnLeft(isLayoutRtl()) ? shift : -shift;

        ValueAnimator positionAnim = new SpringAnimationBuilder(getContext())
                .setStartValue(startTx)
                .setEndValue(0)
                .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
                .setStiffness(FADE_IN_ANIM_POSITION_SPRING_STIFFNESS)
                .build(this, VIEW_TRANSLATE_X);

        ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(this, ALPHA, 1f)
                .setDuration(FADE_IN_ANIM_ALPHA_DURATION_MS);

        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playTogether(positionAnim, alphaAnim);
        return animatorSet;
    }

    /**
     * Updates the bounds with translation that may have been applied and returns the result.
     */
    public Rect getBubbleBarBounds() {
        mBubbleBarBounds.top = getTop() + (int) getTranslationY();
        mBubbleBarBounds.top = getTop() + (int) getTranslationY() + mPointerSize;
        mBubbleBarBounds.bottom = getBottom() + (int) getTranslationY();
        return mBubbleBarBounds;
    }
@@ -290,7 +422,7 @@ public class BubbleBarView extends FrameLayout {
        int bubbleCount = getChildCount();
        final float ty = (mBubbleBarBounds.height() - mIconSize) / 2f;
        final boolean animate = getVisibility() == VISIBLE;
        final boolean onLeft = isOnLeft();
        final boolean onLeft = mBubbleBarLocation.isOnLeft(isLayoutRtl());
        for (int i = 0; i < bubbleCount; i++) {
            BubbleView bv = (BubbleView) getChildAt(i);
            bv.setTranslationY(ty);
@@ -453,7 +585,7 @@ public class BubbleBarView extends FrameLayout {
    private float arrowPositionForSelectedWhenExpanded() {
        final int index = indexOfChild(mSelectedBubbleView);
        final int bubblePosition;
        if (isOnLeft()) {
        if (mBubbleBarLocation.isOnLeft(isLayoutRtl())) {
            // Bubble positions are reversed. First bubble is on the right.
            bubblePosition = getChildCount() - index - 1;
        } else {
@@ -465,7 +597,7 @@ public class BubbleBarView extends FrameLayout {
    private float arrowPositionForSelectedWhenCollapsed() {
        final int index = indexOfChild(mSelectedBubbleView);
        final int bubblePosition;
        if (isOnLeft()) {
        if (mBubbleBarLocation.isOnLeft(isLayoutRtl())) {
            // Bubble positions are reversed. First bubble may be shifted, if there are more
            // bubbles than the current bubble and overflow.
            bubblePosition = index == 0 && getChildCount() > 2 ? 1 : 0;
Loading