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

Commit 85fca4bb authored by Liran Binyamin's avatar Liran Binyamin
Browse files

Animate dragged bubble to full screen

When dragging a bubble to full screen we now animate the full screen
task to expand from the point where the bubble was dropped.

Flag: com.android.wm.shell.enable_bubble_to_fullscreen
Bug: 388858013
Test: atest BubbleTransitionsTest
Test: manual
       - create bubble in bubble bar
       - drag bubble to full screen
       - observe animation
Change-Id: I3226abeee180d9a8a0a6925cf22f671c05bc9b8f
parent a23ba269
Loading
Loading
Loading
Loading
+5 −4
Original line number Diff line number Diff line
@@ -2643,9 +2643,9 @@ public class BubbleController implements ConfigurationChangeListener,
        mBubbleData.setSelectedBubbleAndExpandStack(bubbleToSelect);
    }

    private void moveBubbleToFullscreen(String key) {
    private void moveDraggedBubbleToFullscreen(String key, Point dropLocation) {
        Bubble b = mBubbleData.getBubbleInStackWithKey(key);
        mBubbleTransitions.startDraggedBubbleIconToFullscreen(b);
        mBubbleTransitions.startDraggedBubbleIconToFullscreen(b, dropLocation);
    }

    private boolean isDeviceLocked() {
@@ -2936,8 +2936,9 @@ public class BubbleController implements ConfigurationChangeListener,
        }

        @Override
        public void moveBubbleToFullscreen(String key) {
            mMainExecutor.execute(() -> mController.moveBubbleToFullscreen(key));
        public void moveDraggedBubbleToFullscreen(String key, Point dropLocation) {
            mMainExecutor.execute(
                    () -> mController.moveDraggedBubbleToFullscreen(key, dropLocation));
        }
    }

+50 −4
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.TaskInfo;
import android.content.Context;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.IBinder;
@@ -42,6 +43,11 @@ import android.window.TransitionRequestInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;

import androidx.core.animation.Animator;
import androidx.core.animation.Animator.AnimatorUpdateListener;
import androidx.core.animation.AnimatorListenerAdapter;
import androidx.core.animation.ValueAnimator;

import com.android.internal.annotations.VisibleForTesting;
import com.android.launcher3.icons.BubbleIconFactory;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -114,8 +120,8 @@ public class BubbleTransitions {
    }

    /** Starts a transition that converts a dragged bubble icon to a full screen task. */
    public BubbleTransition startDraggedBubbleIconToFullscreen(Bubble bubble) {
        return new DraggedBubbleIconToFullscreen(bubble);
    public BubbleTransition startDraggedBubbleIconToFullscreen(Bubble bubble, Point dropLocation) {
        return new DraggedBubbleIconToFullscreen(bubble, dropLocation);
    }

    /**
@@ -660,9 +666,19 @@ public class BubbleTransitions {

        IBinder mTransition;
        final Bubble mBubble;
        final Point mDropLocation;
        final TransactionProvider mTransactionProvider;

        DraggedBubbleIconToFullscreen(Bubble bubble, Point dropLocation) {
            this(bubble, dropLocation, SurfaceControl.Transaction::new);
        }

        DraggedBubbleIconToFullscreen(Bubble bubble) {
        @VisibleForTesting
        DraggedBubbleIconToFullscreen(Bubble bubble, Point dropLocation,
                TransactionProvider transactionProvider) {
            mBubble = bubble;
            mDropLocation = dropLocation;
            mTransactionProvider = transactionProvider;
            bubble.setPreparingTransition(this);
            WindowContainerToken token = bubble.getTaskView().getTaskInfo().getToken();
            WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -710,8 +726,34 @@ public class BubbleTransitions {
            }
            mRepository.remove(taskViewTaskController);

            final SurfaceControl taskLeash = change.getLeash();
            // set the initial position of the task with 0 scale
            startTransaction.setPosition(taskLeash, mDropLocation.x, mDropLocation.y);
            startTransaction.setScale(taskLeash, 0, 0);
            startTransaction.apply();

            final SurfaceControl.Transaction animT = mTransactionProvider.get();
            ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
            animator.setDuration(250);
            animator.addUpdateListener(new AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(@NonNull Animator animation) {
                    float progress = animator.getAnimatedFraction();
                    float x = mDropLocation.x * (1 - progress);
                    float y = mDropLocation.y * (1 - progress);
                    animT.setPosition(taskLeash, x, y);
                    animT.setScale(taskLeash, progress, progress);
                    animT.apply();
                }
            });
            animator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(@NonNull Animator animation) {
                    animT.close();
                    finishCallback.onTransitionFinished(null);
                }
            });
            animator.start();
            taskViewTaskController.notifyTaskRemovalStarted(mBubble.getTaskView().getTaskInfo());
            mTaskViewTransitions.onExternalDone(transition);
            return true;
@@ -761,4 +803,8 @@ public class BubbleTransitions {
            mTaskViewTransitions.onExternalDone(transition);
        }
    }

    interface TransactionProvider {
        SurfaceControl.Transaction get();
    }
}
+2 −1
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.wm.shell.bubbles;

import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.UserHandle;
import com.android.wm.shell.bubbles.IBubblesListener;
@@ -59,5 +60,5 @@ interface IBubbles {

    oneway void showDropTarget(in boolean show, in @nullable BubbleBarLocation location) = 15;

    oneway void moveBubbleToFullscreen(in String key) = 16;
    oneway void moveDraggedBubbleToFullscreen(in String key, in Point dropLocation) = 16;
}
 No newline at end of file
+26 −6
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -37,6 +38,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.app.ActivityManager;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.IBinder;
@@ -47,7 +49,9 @@ import android.window.TransitionInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;

import androidx.core.animation.AnimatorTestRule;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;

import com.android.launcher3.icons.BubbleIconFactory;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -65,6 +69,7 @@ import com.android.wm.shell.taskview.TaskViewTransitions;
import com.android.wm.shell.transition.Transitions;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
@@ -76,6 +81,8 @@ import org.mockito.MockitoAnnotations;
@SmallTest
public class BubbleTransitionsTest extends ShellTestCase {

    @Rule public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule();

    private static final int FULLSCREEN_TASK_WIDTH = 200;
    private static final int FULLSCREEN_TASK_HEIGHT = 100;

@@ -292,23 +299,36 @@ public class BubbleTransitionsTest extends ShellTestCase {
    @Test
    public void convertDraggedBubbleToFullscreen() {
        ActivityManager.RunningTaskInfo taskInfo = setupBubble();
        SurfaceControl.Transaction animT = mock(SurfaceControl.Transaction.class);
        BubbleTransitions.TransactionProvider transactionProvider = () -> animT;
        final DraggedBubbleIconToFullscreen bt =
                (DraggedBubbleIconToFullscreen) mBubbleTransitions
                        .startDraggedBubbleIconToFullscreen(mBubble);
                mBubbleTransitions.new DraggedBubbleIconToFullscreen(
                        mBubble, new Point(100, 50), transactionProvider);
        verify(mTransitions).startTransition(anyInt(), any(), eq(bt));

        SurfaceControl taskLeash = new SurfaceControl.Builder().setName("taskLeash").build();
        final TransitionInfo info = new TransitionInfo(TRANSIT_TO_FRONT, 0);
        final TransitionInfo.Change chg = new TransitionInfo.Change(taskInfo.token,
                mock(SurfaceControl.class));
        final TransitionInfo.Change chg = new TransitionInfo.Change(taskInfo.token, taskLeash);
        chg.setMode(TRANSIT_TO_FRONT);
        chg.setTaskInfo(taskInfo);
        info.addChange(chg);
        info.addRoot(new TransitionInfo.Root(0, mock(SurfaceControl.class), 0, 0));
        SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
        SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
        Transitions.TransitionFinishCallback finishCb = wct -> {};
        boolean[] transitionFinished = {false};
        Transitions.TransitionFinishCallback finishCb = wct -> transitionFinished[0] = true;
        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
            bt.startAnimation(bt.mTransition, info, startT, finishT, finishCb);
            mAnimatorTestRule.advanceTimeBy(250);
        });
        verify(startT).setScale(taskLeash, 0, 0);
        verify(startT).setPosition(taskLeash, 100, 50);
        verify(startT).apply();
        verify(animT).setScale(taskLeash, 1, 1);
        verify(animT).setPosition(taskLeash, 0, 0);
        verify(animT, atLeastOnce()).apply();
        verify(animT).close();
        assertFalse(mTaskViewTransitions.hasPending());
        assertTrue(transitionFinished[0]);
    }
}