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

Commit c69bd224 authored by Jorim Jaggi's avatar Jorim Jaggi
Browse files

Implement transition for docking task in recents #6

- Use a future to provide the app thumbnail so the app can restart
in parallel when recents draws the bitmap (extremely expensive).
- Don't call startRecents from AM when recents is already running - this
messes up the transition information.
- Make sure to put the task into resizing mode if it needs to be restored
from the disk.
- Some minor fixes for the transition animation spec.
- Add NO_MOVE_ANIMATION to recents flags to prevent wallpaper
flickering.

Bug: 27607141
Change-Id: I7d0c75b88775ab467927b8cf94303ddb60222e7f
parent 787e9dd6
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -20,8 +20,9 @@ import java.util.List;
/**
 * This interface abstracts a collection of Keyframe objects and is called by
 * ValueAnimator to calculate values between those keyframes for a given animation.
 * @hide
 */
interface Keyframes extends Cloneable {
public interface Keyframes extends Cloneable {

    /**
     * Sets the TypeEvaluator to be used when calculating animated values. This object
+3 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import android.view.KeyEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;

import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.MetricsProto.MetricsEvent;
@@ -282,6 +283,8 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        registerReceiver(mSystemBroadcastReceiver, filter);

        getWindow().addPrivateFlags(LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION);
    }

    @Override
+16 −8
Original line number Diff line number Diff line
@@ -25,7 +25,6 @@ import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDO
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.ActivityOptions;
import android.app.ActivityOptions.OnAnimationStartedListener;
import android.app.AppGlobals;
import android.app.IActivityManager;
import android.app.ITaskStackListener;
@@ -53,6 +52,7 @@ import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.IRemoteCallback;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
@@ -65,9 +65,8 @@ import android.util.ArraySet;
import android.util.Log;
import android.util.MutableBoolean;
import android.view.Display;
import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.IDockedStackListener;
import android.view.Surface;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManager.KeyboardShortcutsReceiver;
import android.view.WindowManagerGlobal;
@@ -402,15 +401,12 @@ public class SystemServicesProxy {
    }

    /** Docks a task to the side of the screen and starts it. */
    public void startTaskInDockedMode(Context context, View view, int taskId, int createMode,
            Bitmap headerBitmap, Handler handler, OnAnimationStartedListener startedListener) {
    public void startTaskInDockedMode(int taskId, int createMode) {
        if (mIam == null) return;

        try {
            // TODO: Determine what animation we want for the incoming task
            final ActivityOptions options = ActivityOptions.makeThumbnailAspectScaleUpAnimation(
                    view, headerBitmap, 0, 0, (int) (view.getWidth() * view.getScaleX()),
                    (int) (view.getHeight() * view.getScaleY()), handler, startedListener);
            final ActivityOptions options = ActivityOptions.makeBasic();
            options.setDockCreateMode(createMode);
            options.setLaunchStackId(DOCKED_STACK_ID);
            mIam.startActivityFromRecents(taskId, options.toBundle());
@@ -1056,6 +1052,18 @@ public class SystemServicesProxy {
        }
    }

    public void overridePendingAppTransitionMultiThumbFuture(
            IAppTransitionAnimationSpecsFuture future, IRemoteCallback animStartedListener,
            boolean scaleUp) {
        try {
            WindowManagerGlobal.getWindowManagerService()
                    .overridePendingAppTransitionMultiThumbFuture(future, animStartedListener,
                            scaleUp);
        } catch (RemoteException e) {
            Log.w(TAG, "Failed to override transition: " + e);
        }
    }

    private final class H extends Handler {
        private static final int ON_TASK_STACK_CHANGED = 1;
        private static final int ON_ACTIVITY_PINNED = 2;
+51 −34
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import android.annotation.Nullable;
import android.app.ActivityManager.StackId;
import android.app.ActivityOptions;
import android.app.ActivityOptions.OnAnimationStartedListener;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -32,10 +33,8 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.IRemoteCallback;
import android.os.RemoteException;
import android.util.Log;
import android.view.AppTransitionAnimationSpec;
import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.WindowManagerGlobal;

import com.android.internal.annotations.GuardedBy;
import com.android.systemui.recents.Recents;
@@ -52,6 +51,7 @@ import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
@@ -92,7 +92,7 @@ public class RecentsTransitionHelper {
     */
    public void launchTaskFromRecents(final TaskStack stack, @Nullable final Task task,
            final TaskStackView stackView, final TaskView taskView,
            final boolean screenPinningRequested, final Rect bounds, int destinationStack) {
            final boolean screenPinningRequested, final Rect bounds, final int destinationStack) {
        final ActivityOptions opts = ActivityOptions.makeBasic();
        if (bounds != null) {
            opts.setLaunchBounds(bounds.isEmpty() ? null : bounds);
@@ -101,7 +101,12 @@ public class RecentsTransitionHelper {
        final ActivityOptions.OnAnimationStartedListener animStartedListener;
        final IAppTransitionAnimationSpecsFuture transitionFuture;
        if (taskView != null) {
            transitionFuture = getAppTransitionFuture(task, stackView, destinationStack);
            transitionFuture = getAppTransitionFuture(new AnimationSpecComposer() {
                @Override
                public List<AppTransitionAnimationSpec> composeSpecs() {
                    return composeAnimationSpecs(task, stackView, destinationStack);
                }
            });
            animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
                @Override
                public void onAnimationStarted() {
@@ -154,6 +159,23 @@ public class RecentsTransitionHelper {
        }
    }

    public IRemoteCallback wrapStartedListener(final OnAnimationStartedListener listener) {
        if (listener == null) {
            return null;
        }
        return new IRemoteCallback.Stub() {
            @Override
            public void sendResult(Bundle data) throws RemoteException {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        listener.onAnimationStarted();
                    }
                });
            }
        };
    }

    /**
     * Starts the activity for the launch task.
     *
@@ -181,40 +203,21 @@ public class RecentsTransitionHelper {
        }

        if (transitionFuture != null) {
            IRemoteCallback.Stub callback = null;
            if (animStartedListener != null) {
                callback = new IRemoteCallback.Stub() {
                    @Override
                    public void sendResult(Bundle data) throws RemoteException {
                        mHandler.post(new Runnable() {
                            @Override
                            public void run() {
                                if (animStartedListener != null) {
                                    animStartedListener.onAnimationStarted();
                                }
                            }
                        });
                    }
                };
            }
            try {
                synchronized (this) {
                    mAppTransitionAnimationSpecs = SPECS_WAITING;
                }
                WindowManagerGlobal.getWindowManagerService()
                        .overridePendingAppTransitionMultiThumbFuture(transitionFuture,
                                callback, true /* scaleUp */);
            } catch (RemoteException e) {
                Log.w(TAG, "Failed to override transition: " + e);
            }
            ssp.overridePendingAppTransitionMultiThumbFuture(transitionFuture,
                    wrapStartedListener(animStartedListener), true /* scaleUp */);
        }
    }

    /**
     * Creates a future which will later be queried for animation specs for this current transition.
     *
     * @param composer The implementation that composes the specs on the UI thread.
     */
    private IAppTransitionAnimationSpecsFuture getAppTransitionFuture(final Task task,
            final TaskStackView stackView, final int destinationStack) {
    public IAppTransitionAnimationSpecsFuture getAppTransitionFuture(
            final AnimationSpecComposer composer) {
        synchronized (this) {
            mAppTransitionAnimationSpecs = SPECS_WAITING;
        }
        return new IAppTransitionAnimationSpecsFuture.Stub() {
            @Override
            public AppTransitionAnimationSpec[] get() throws RemoteException {
@@ -222,8 +225,7 @@ public class RecentsTransitionHelper {
                    @Override
                    public void run() {
                        synchronized (RecentsTransitionHelper.this) {
                            mAppTransitionAnimationSpecs = composeAnimationSpecs(task, stackView,
                                    destinationStack);
                            mAppTransitionAnimationSpecs = composer.composeSpecs();
                            RecentsTransitionHelper.this.notifyAll();
                        }
                    }
@@ -247,6 +249,17 @@ public class RecentsTransitionHelper {
        };
    }

    /**
     * Composes the transition spec when docking a task, which includes a full task bitmap.
     */
    public List<AppTransitionAnimationSpec> composeDockAnimationSpec(
            TaskView taskView, Rect transform) {
        TaskViewTransform viewTransform = new TaskViewTransform();
        viewTransform.fillIn(taskView);
        return Collections.singletonList(new AppTransitionAnimationSpec(taskView.getTask().key.id,
                RecentsTransitionHelper.composeTaskBitmap(taskView, viewTransform), transform));
    }

    /**
     * Composes the animation specs for all the tasks in the target stack.
     */
@@ -374,4 +387,8 @@ public class RecentsTransitionHelper {
        }
        return new AppTransitionAnimationSpec(taskView.getTask().key.id, b, taskRect);
    }

    public interface AnimationSpecComposer {
        List<AppTransitionAnimationSpec> composeSpecs();
    }
}
+33 −23
Original line number Diff line number Diff line
@@ -29,9 +29,14 @@ import android.graphics.Outline;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.IRemoteCallback;
import android.os.RemoteException;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.view.AppTransitionAnimationSpec;
import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -39,9 +44,11 @@ import android.view.ViewDebug;
import android.view.ViewOutlineProvider;
import android.view.ViewPropertyAnimator;
import android.view.WindowInsets;
import android.view.WindowManagerGlobal;
import android.widget.FrameLayout;
import android.widget.TextView;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.MetricsProto.MetricsEvent;
import com.android.systemui.Interpolators;
@@ -70,6 +77,7 @@ import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.recents.views.RecentsTransitionHelper.AnimationSpecComposer;
import com.android.systemui.stackdivider.WindowManagerProxy;
import com.android.systemui.statusbar.FlingAnimationUtils;

@@ -486,13 +494,6 @@ public class RecentsView extends FrameLayout {
            event.taskView.setLeftTopRightBottom(taskViewRect.left, taskViewRect.top,
                    taskViewRect.right, taskViewRect.bottom);

            // Remove the task view after it is docked
            mTaskStackView.updateLayoutAlgorithm(false /* boundScroll */);
            stackLayout.getStackTransform(event.task, stackScroller.getStackScroll(), tmpTransform,
                    null);
            tmpTransform.alpha = 0;
            tmpTransform.scale = TaskStackView.DRAG_SCALE_FACTOR;
            tmpTransform.rect.set(taskViewRect);
            final OnAnimationStartedListener startedListener = new OnAnimationStartedListener() {
                @Override
                public void onAnimationStarted() {
@@ -501,22 +502,22 @@ public class RecentsView extends FrameLayout {
                            true /* fromDockGesture */);
                }
            };
            mTaskStackView.updateTaskViewToTransform(event.taskView, tmpTransform,
                    new AnimationProps(125, Interpolators.ALPHA_OUT,
                            new AnimatorListenerAdapter() {
                                @Override
                                public void onAnimationEnd(Animator animation) {

            // Dock the task and launch it
            SystemServicesProxy ssp = Recents.getSystemServices();
                                    TaskViewTransform transform = new TaskViewTransform();
                                    transform.fillIn(event.taskView);
                                    ssp.startTaskInDockedMode(getContext(), event.taskView,
                                            event.task.key.id, dockState.createMode,
                                            RecentsTransitionHelper.composeTaskBitmap(
                                                    event.taskView, transform),
                                            mHandler, startedListener);
            ssp.startTaskInDockedMode(event.task.key.id, dockState.createMode);
            final Rect taskRect = getTaskRect(event.taskView);
            IAppTransitionAnimationSpecsFuture future = mTransitionHelper.getAppTransitionFuture(
                    new AnimationSpecComposer() {
                        @Override
                        public List<AppTransitionAnimationSpec> composeSpecs() {
                            return mTransitionHelper.composeDockAnimationSpec(
                                    event.taskView, taskRect);
                        }
                            }));
                    });
            ssp.overridePendingAppTransitionMultiThumbFuture(future,
                    mTransitionHelper.wrapStartedListener(startedListener),
                    true /* scaleUp */);

            MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_DRAG_DROP);
        } else {
@@ -526,6 +527,15 @@ public class RecentsView extends FrameLayout {
        }
    }

    private Rect getTaskRect(TaskView taskView) {
        int[] location = taskView.getLocationOnScreen();
        int viewX = location[0];
        int viewY = location[1];
        return new Rect(viewX, viewY,
                (int) (viewX + taskView.getWidth() * taskView.getScaleX()),
                (int) (viewY + taskView.getHeight() * taskView.getScaleY()));
    }

    public final void onBusEvent(DraggingInRecentsEvent event) {
        if (mTaskStackView.getTaskViews().size() > 0) {
            setTranslationY(event.distanceFromTop - mTaskStackView.getTaskViews().get(0).getY());
Loading