OSDN Git Service

Implement transition for docking task in recents #6
authorJorim Jaggi <jjaggi@google.com>
Tue, 15 Mar 2016 13:38:37 +0000 (14:38 +0100)
committerJorim Jaggi <jjaggi@google.com>
Fri, 25 Mar 2016 22:13:36 +0000 (15:13 -0700)
- 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

core/java/android/animation/Keyframes.java
packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
services/core/java/com/android/server/am/ActivityManagerService.java
services/core/java/com/android/server/am/ActivityStackSupervisor.java
services/core/java/com/android/server/am/ActivityStarter.java
services/core/java/com/android/server/wm/AppTransition.java
services/core/java/com/android/server/wm/WindowLayersController.java

index c149bed..e40a86c 100644 (file)
@@ -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
index 363532e..9cba93b 100644 (file)
@@ -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
index 9f93dbb..1e624fd 100644 (file)
@@ -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;
index c661a2b..7c5b441 100644 (file)
@@ -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();
                         }
                     }
@@ -248,6 +250,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.
      */
     private List<AppTransitionAnimationSpec> composeAnimationSpecs(final Task task,
@@ -374,4 +387,8 @@ public class RecentsTransitionHelper {
         }
         return new AppTransitionAnimationSpec(taskView.getTask().key.id, b, taskRect);
     }
+
+    public interface AnimationSpecComposer {
+        List<AppTransitionAnimationSpec> composeSpecs();
+    }
 }
index 5a27dfa..28880df 100644 (file)
@@ -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);
-                                }
-                            }));
+
+            // Dock the task and launch it
+            SystemServicesProxy ssp = Recents.getSystemServices();
+            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());
index 6841ae9..3d13715 100644 (file)
@@ -11392,10 +11392,12 @@ public final class ActivityManagerService extends ActivityManagerNative
                     // Get the focused task before launching launcher.
 
                     if (mUserController.isLockScreenDisabled(currentUserId)) {
+
                         // If there is no device lock, we will show the profile's credential page.
                         // startActivityFromRecentsInner is intercepted and will forward user to it.
                         if (mFocusedActivity != null) {
-                            startActivityFromRecentsInner(mFocusedActivity.task.taskId, null);
+                            mStackSupervisor.startActivityFromRecentsInner(
+                                    mFocusedActivity.task.taskId, null);
                         }
                     } else {
                         // Showing launcher to avoid user entering credential twice.
index 22e10b1..0e6d174 100644 (file)
@@ -4187,6 +4187,16 @@ public final class ActivityStackSupervisor implements DisplayListener {
                 + " in=" + stacks);
     }
 
+    /**
+     * Puts a task into resizing mode during the next app transition.
+     *
+     * @param taskId the id of the task to put into resizing mode
+     */
+    private void setResizingDuringAnimation(int taskId) {
+        mResizingTasksDuringAnimation.add(taskId);
+        mWindowManager.setTaskDockedResizing(taskId, true);
+    }
+
     final int startActivityFromRecentsInner(int taskId, Bundle bOptions) {
         final TaskRecord task;
         final int callingUid;
@@ -4236,8 +4246,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
             // the window renders full-screen with the background filling the void. Also only
             // call this at the end to make sure that tasks exists on the window manager side.
             if (launchStackId == DOCKED_STACK_ID) {
-                mResizingTasksDuringAnimation.add(task.taskId);
-                mWindowManager.setTaskDockedResizing(task.taskId, true);
+                setResizingDuringAnimation(taskId);
             }
             return ActivityManager.START_TASK_TO_FRONT;
         }
@@ -4246,7 +4255,11 @@ public final class ActivityStackSupervisor implements DisplayListener {
         intent = task.intent;
         intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
         userId = task.userId;
-        return mService.startActivityInPackage(callingUid, callingPackage, intent, null, null, null,
-                0, 0, bOptions, userId, null, task);
+            int result = mService.startActivityInPackage(callingUid, callingPackage, intent, null,
+                    null, null, 0, 0, bOptions, userId, null, task);
+            if (launchStackId == DOCKED_STACK_ID) {
+                setResizingDuringAnimation(task.taskId);
+            }
+            return result;
     }
 }
index f244c93..7be9f9d 100644 (file)
@@ -547,12 +547,18 @@ class ActivityStarter {
         }
 
         if (startedActivityStackId == DOCKED_STACK_ID && prevFocusedStackId == HOME_STACK_ID) {
-            // We launch an activity while being in home stack, which means either launcher or
-            // recents into docked stack. We don't want the launched activity to be alone in a
-            // docked stack, so we want to immediately launch recents too.
-            if (DEBUG_RECENTS) Slog.d(TAG, "Scheduling recents launch.");
-            mWindowManager.showRecentApps();
-            return;
+            final ActivityStack homeStack = mSupervisor.getStack(HOME_STACK_ID);
+            final ActivityRecord topActivityHomeStack = homeStack != null
+                    ? homeStack.topRunningActivityLocked() : null;
+            if (topActivityHomeStack == null
+                    || topActivityHomeStack.mActivityType != RECENTS_ACTIVITY_TYPE) {
+                // We launch an activity while being in home stack, which means either launcher or
+                // recents into docked stack. We don't want the launched activity to be alone in a
+                // docked stack, so we want to immediately launch recents too.
+                if (DEBUG_RECENTS) Slog.d(TAG, "Scheduling recents launch.");
+                mWindowManager.showRecentApps();
+                return;
+            }
         }
 
         if (startedActivityStackId == PINNED_STACK_ID
index 9536cb7..62fa013 100644 (file)
@@ -72,7 +72,6 @@ import android.view.animation.PathInterpolator;
 import android.view.animation.ScaleAnimation;
 import android.view.animation.TranslateAnimation;
 
-import com.android.internal.R;
 import com.android.internal.util.DumpUtils.Dump;
 import com.android.server.AttributeCache;
 import com.android.server.wm.WindowManagerService.H;
@@ -147,6 +146,9 @@ public class AppTransition implements Dump {
     static final Interpolator TOUCH_RESPONSE_INTERPOLATOR =
             new PathInterpolator(0.3f, 0f, 0.1f, 1f);
 
+    private static final Interpolator THUMBNAIL_DOCK_INTERPOLATOR =
+            new PathInterpolator(0.85f, 0f, 1f, 1f);
+
     /**
      * Maximum duration for the clip reveal animation. This is used when there is a lot of movement
      * involved, to make it more understandable.
@@ -827,7 +829,9 @@ public class AppTransition implements Dump {
             a.setDuration(duration);
         }
         a.setFillAfter(true);
-        a.setInterpolator(interpolator);
+        if (interpolator != null) {
+            a.setInterpolator(interpolator);
+        }
         a.initialize(appWidth, appHeight, appWidth, appHeight);
         return a;
     }
@@ -900,8 +904,11 @@ public class AppTransition implements Dump {
             scale.setInterpolator(interpolator);
             scale.setDuration(duration);
             Animation alpha = new AlphaAnimation(1f, 0f);
-            alpha.setInterpolator(mThumbnailFadeOutInterpolator);
-            alpha.setDuration(duration);
+            alpha.setInterpolator(mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS
+                    ? THUMBNAIL_DOCK_INTERPOLATOR : mThumbnailFadeOutInterpolator);
+            alpha.setDuration(mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS
+                    ? duration / 2
+                    : duration);
             Animation translate = createCurvedMotion(fromX, toX, fromY, toY);
             translate.setInterpolator(interpolator);
             translate.setDuration(duration);
@@ -912,8 +919,8 @@ public class AppTransition implements Dump {
             // Containing frame is in screen space, but we need the clip rect in the
             // app space.
             mTmpToClipRect.offsetTo(0, 0);
-            mTmpToClipRect.right = (int) (mTmpToClipRect.right * scaleW);
-            mTmpToClipRect.bottom = (int) (mTmpToClipRect.bottom * scaleW);
+            mTmpToClipRect.right = (int) (mTmpToClipRect.right / scaleW);
+            mTmpToClipRect.bottom = (int) (mTmpToClipRect.bottom / scaleW);
 
             if (contentInsets != null) {
                 mTmpToClipRect.inset((int) (-contentInsets.left * scaleW),
@@ -954,13 +961,13 @@ public class AppTransition implements Dump {
 
         }
         return prepareThumbnailAnimationWithDuration(a, appWidth, appRect.height(), 0,
-                TOUCH_RESPONSE_INTERPOLATOR);
+                null);
     }
 
     private Animation createCurvedMotion(float fromX, float toX, float fromY, float toY) {
 
         // Almost no x-change - use linear animation
-        if (Math.abs(toX - fromX) < 1f) {
+        if (Math.abs(toX - fromX) < 1f || mNextAppTransition != TRANSIT_DOCK_TASK_FROM_RECENTS) {
             return new TranslateAnimation(fromX, toX, fromY, toY);
         } else {
             final Path path = createCurvedPath(fromX, toX, fromY, toY);
@@ -977,7 +984,7 @@ public class AppTransition implements Dump {
 
     private long getAspectScaleDuration() {
         if (mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS) {
-            return (long) (THUMBNAIL_APP_TRANSITION_DURATION * 1f);
+            return (long) (THUMBNAIL_APP_TRANSITION_DURATION * 1.35f);
         } else {
             return THUMBNAIL_APP_TRANSITION_DURATION;
         }
@@ -1009,10 +1016,6 @@ public class AppTransition implements Dump {
         final int thumbStartX = mTmpRect.left - containingFrame.left;
         final int thumbStartY = mTmpRect.top - containingFrame.top;
 
-        // Used for the ENTER_SCALE_UP and EXIT_SCALE_DOWN transitions
-        float scale = 1f;
-        int scaledTopDecor = 0;
-
         switch (thumbTransitState) {
             case THUMBNAIL_TRANSITION_ENTER_SCALE_UP:
             case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
@@ -1041,25 +1044,34 @@ public class AppTransition implements Dump {
 
                     if (orientation == Configuration.ORIENTATION_PORTRAIT) {
                         // We scale the width and clip to the top/left square
+                        // We scale the width and clip to the top/left square
+                        float scale = thumbWidth /
+                                (appWidth - contentInsets.left - contentInsets.right);
+                        int unscaledThumbHeight = (int) (thumbHeight / scale);
+                        mTmpFromClipRect.bottom = mTmpFromClipRect.top + unscaledThumbHeight;
+
+                        mNextAppTransitionInsets.set(contentInsets);
+
                         Animation scaleAnim = new ScaleAnimation(
                                 scaleUp ? scale : 1, scaleUp ? 1 : scale,
                                 scaleUp ? scale : 1, scaleUp ? 1 : scale,
-                                containingFrame.width() / 2,
-                                containingFrame.height() / 2 + contentInsets.top);
+                                containingFrame.width() / 2f,
+                                containingFrame.height() / 2f + contentInsets.top);
                         final float targetX = (mTmpRect.left - containingFrame.left);
-                        final float x = containingFrame.width() / 2
-                                - containingFrame.width() / 2 * scale;
+                        final float x = containingFrame.width() / 2f
+                                - containingFrame.width() / 2f * scale;
                         final float targetY = (mTmpRect.top - containingFrame.top);
-                        final float y = containingFrame.height() / 2
-                                - containingFrame.height() / 2 * scale;
+                        final float y = containingFrame.height() / 2f
+                                - containingFrame.height() / 2f * scale;
                         final float startX = targetX - x;
                         final float startY = targetY - y;
                         Animation clipAnim = scaleUp
                                 ? new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect)
                                 : new ClipRectAnimation(mTmpToClipRect, mTmpFromClipRect);
                         Animation translateAnim = scaleUp
-                                ? createCurvedMotion(startX, 0, startY - scaledTopDecor, 0)
-                                : createCurvedMotion(0, startX, 0, startY - scaledTopDecor);
+                                ? createCurvedMotion(startX, 0, startY - contentInsets.top, 0)
+                                : createCurvedMotion(0, startX, 0, startY - contentInsets.top);
+
                         set.addAnimation(clipAnim);
                         set.addAnimation(scaleAnim);
                         set.addAnimation(translateAnim);
@@ -1081,7 +1093,6 @@ public class AppTransition implements Dump {
                         set.addAnimation(clipAnim);
                         set.addAnimation(translateAnim);
                     }
-                    set.setZAdjustment(Animation.ZORDER_TOP);
                     a = set;
                     a.setZAdjustment(Animation.ZORDER_TOP);
                 }
@@ -1527,6 +1538,7 @@ public class AppTransition implements Dump {
 
     int getAppStackClipMode() {
         return mNextAppTransition == TRANSIT_ACTIVITY_RELAUNCH
+                || mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS
                 ? STACK_CLIP_NONE
                 : STACK_CLIP_AFTER_ANIM;
     }
index e018a4e..f76f03f 100644 (file)
@@ -227,6 +227,10 @@ public class WindowLayersController {
         w.mLayer = layer;
         w.mWinAnimator.mAnimLayer = w.mLayer + w.getAnimLayerAdjustment() +
                     getSpecialWindowAnimLayerAdjustment(w);
+        if (w.mAppToken != null && w.mAppToken.mAppAnimator.thumbnailForceAboveLayer > 0
+                && w.mWinAnimator.mAnimLayer > w.mAppToken.mAppAnimator.thumbnailForceAboveLayer) {
+            w.mAppToken.mAppAnimator.thumbnailForceAboveLayer = w.mWinAnimator.mAnimLayer;
+        }
     }
 
     void dump(PrintWriter pw, String s) {