Merge "Add app to overview anim for Recents Go." into ub-launcher3-master
diff --git a/go/quickstep/res/layout/task_item_view.xml b/go/quickstep/res/layout/task_item_view.xml
index ee67d49..048e9c5 100644
--- a/go/quickstep/res/layout/task_item_view.xml
+++ b/go/quickstep/res/layout/task_item_view.xml
@@ -21,15 +21,15 @@
     android:orientation="horizontal">
     <FrameLayout
         android:id="@+id/task_icon_and_thumbnail"
-        android:layout_width="@dimen/task_item_height"
-        android:layout_height="@dimen/task_item_height"
+        android:layout_width="@dimen/task_thumbnail_and_icon_view_size"
+        android:layout_height="@dimen/task_thumbnail_and_icon_view_size"
         android:layout_gravity="center_vertical"
         android:layout_marginHorizontal="8dp"
         android:layout_marginVertical="@dimen/task_item_half_vert_margin">
         <ImageView
             android:id="@+id/task_thumbnail"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
+            android:layout_width="@dimen/task_thumbnail_width"
+            android:layout_height="@dimen/task_thumbnail_height"
             android:layout_gravity="top|start"/>
         <ImageView
             android:id="@+id/task_icon"
diff --git a/go/quickstep/res/values/dimens.xml b/go/quickstep/res/values/dimens.xml
index 28cc1eb..7269704 100644
--- a/go/quickstep/res/values/dimens.xml
+++ b/go/quickstep/res/values/dimens.xml
@@ -15,7 +15,9 @@
      limitations under the License.
 -->
 <resources>
-    <dimen name="task_item_height">60dp</dimen>
     <dimen name="task_item_half_vert_margin">8dp</dimen>
+    <dimen name="task_thumbnail_and_icon_view_size">60dp</dimen>
+    <dimen name="task_thumbnail_height">60dp</dimen>
+    <dimen name="task_thumbnail_width">36dp</dimen>
     <dimen name="task_icon_size">36dp</dimen>
 </resources>
\ No newline at end of file
diff --git a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index f199643..defed84 100644
--- a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -15,15 +15,29 @@
  */
 package com.android.quickstep;
 
+import static com.android.launcher3.anim.Interpolators.ACCEL_2;
+import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.quickstep.util.RemoteAnimationProvider.getLayer;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
 
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
+import android.graphics.Matrix;
+import android.graphics.Rect;
 import android.util.Log;
+import android.view.View;
+
+import androidx.annotation.NonNull;
 
 import com.android.launcher3.BaseDraggingActivity;
+import com.android.quickstep.util.MultiValueUpdateListener;
 import com.android.quickstep.util.RemoteAnimationProvider;
+import com.android.quickstep.views.IconRecentsView;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
 
 /**
  * Provider for the atomic remote window animation from the app to the overview.
@@ -33,13 +47,17 @@
 final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> implements
         RemoteAnimationProvider {
 
-    private static final long RECENTS_LAUNCH_DURATION = 250;
+    private static final long APP_TO_THUMBNAIL_FADE_DURATION = 50;
+    private static final long APP_SCALE_DOWN_DURATION = 400;
     private static final String TAG = "AppToOverviewAnimationProvider";
 
     private final ActivityControlHelper<T> mHelper;
+    private final int mTargetTaskId;
+    private IconRecentsView mRecentsView;
 
     AppToOverviewAnimationProvider(ActivityControlHelper<T> helper, int targetTaskId) {
         mHelper = helper;
+        mTargetTaskId = targetTaskId;
     }
 
     /**
@@ -54,35 +72,157 @@
                         false /* animate activity */, (controller) -> {
                             controller.dispatchOnStart();
                             ValueAnimator anim = controller.getAnimationPlayer()
-                                    .setDuration(RECENTS_LAUNCH_DURATION);
+                                    .setDuration(getRecentsLaunchDuration());
                             anim.setInterpolator(FAST_OUT_SLOW_IN);
                             anim.start();
                         });
         factory.onRemoteAnimationReceived(null);
-        factory.createActivityController(RECENTS_LAUNCH_DURATION);
+        factory.createActivityController(getRecentsLaunchDuration());
+        mRecentsView = activity.getOverviewPanel();
         return false;
     }
 
     /**
-     * Create remote window animation from the currently running app to the overview panel.
+     * Create remote window animation from the currently running app to the overview panel. Should
+     * be called after {@link #onActivityReady}.
      *
      * @param targetCompats the target apps
      * @return animation from app to overview
      */
     @Override
     public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targetCompats) {
-        //TODO: Implement the overview to app window animation for Go.
         AnimatorSet anim = new AnimatorSet();
-        anim.play(ValueAnimator.ofInt(0, 1).setDuration(RECENTS_LAUNCH_DURATION));
+        if (mRecentsView == null) {
+            if (Log.isLoggable(TAG, Log.WARN)) {
+                Log.w(TAG, "No recents view. Using stub animation.");
+            }
+            anim.play(ValueAnimator.ofInt(0, 1).setDuration(getRecentsLaunchDuration()));
+            return anim;
+        }
+
+        RemoteAnimationTargetCompat recentsTarget = null;
+        RemoteAnimationTargetCompat closingAppTarget = null;
+
+        for (RemoteAnimationTargetCompat target : targetCompats) {
+            if (target.mode == MODE_OPENING) {
+                recentsTarget = target;
+            } else if (target.mode == MODE_CLOSING && target.taskId == mTargetTaskId) {
+                closingAppTarget = target;
+            }
+        }
+
+        if (closingAppTarget == null) {
+            if (Log.isLoggable(TAG, Log.WARN)) {
+                Log.w(TAG, "No closing app target. Using stub animation.");
+            }
+            anim.play(ValueAnimator.ofInt(0, 1).setDuration(getRecentsLaunchDuration()));
+            return anim;
+        }
+        if (recentsTarget == null) {
+            if (Log.isLoggable(TAG, Log.WARN)) {
+                Log.w(TAG, "No recents target. Using stub animation.");
+            }
+            anim.play(ValueAnimator.ofInt(0, 1).setDuration(getRecentsLaunchDuration()));
+            return anim;
+        }
+
+        View thumbnailView = mRecentsView.getThumbnailViewForTask(mTargetTaskId);
+        if (thumbnailView == null) {
+            // TODO: We should either 1) guarantee the view is loaded before attempting this
+            // or 2) have a backup animation.
+            if (Log.isLoggable(TAG, Log.WARN)) {
+                Log.w(TAG, "No thumbnail view for running task. Using stub animation.");
+            }
+            anim.play(ValueAnimator.ofInt(0, 1).setDuration(getRecentsLaunchDuration()));
+            return anim;
+        }
+
+        playAppScaleDownAnim(anim, closingAppTarget, recentsTarget, thumbnailView);
+
         return anim;
     }
 
     /**
+     * Animate a closing app to scale down to the location of the thumbnail view in recents.
+     *
+     * @param anim animator set
+     * @param appTarget the app surface thats closing
+     * @param recentsTarget the surface containing recents
+     * @param thumbnailView the thumbnail view to animate to
+     */
+    private void playAppScaleDownAnim(@NonNull AnimatorSet anim,
+            @NonNull RemoteAnimationTargetCompat appTarget,
+            @NonNull RemoteAnimationTargetCompat recentsTarget, @NonNull View thumbnailView) {
+
+        // Identify where the entering remote app should animate to.
+        Rect endRect = new Rect();
+        thumbnailView.getGlobalVisibleRect(endRect);
+
+        Rect appBounds = appTarget.sourceContainerBounds;
+
+        ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 1);
+        valueAnimator.setDuration(APP_SCALE_DOWN_DURATION);
+
+        SyncRtSurfaceTransactionApplierCompat surfaceApplier =
+                new SyncRtSurfaceTransactionApplierCompat(thumbnailView);
+
+        // Keep recents visible throughout the animation.
+        SurfaceParams[] params = new SurfaceParams[2];
+        params[0] = new SurfaceParams(recentsTarget.leash, 1f, null /* matrix */,
+                null /* windowCrop */, getLayer(recentsTarget, MODE_OPENING), 0 /* cornerRadius */);
+
+        valueAnimator.addUpdateListener(new MultiValueUpdateListener() {
+            private final FloatProp mScaleX;
+            private final FloatProp mScaleY;
+            private final FloatProp mTranslationX;
+            private final FloatProp mTranslationY;
+            private final FloatProp mAlpha;
+
+            {
+                // Scale down and move to view location.
+                float endScaleX = ((float) endRect.width()) / appBounds.width();
+                mScaleX = new FloatProp(1f, endScaleX, 0, APP_SCALE_DOWN_DURATION,
+                        ACCEL_DEACCEL);
+                float endScaleY = ((float) endRect.height()) / appBounds.height();
+                mScaleY = new FloatProp(1f, endScaleY, 0, APP_SCALE_DOWN_DURATION,
+                        ACCEL_DEACCEL);
+                float endTranslationX = endRect.left -
+                        (appBounds.width() - thumbnailView.getWidth()) / 2.0f;
+                mTranslationX = new FloatProp(0, endTranslationX, 0, APP_SCALE_DOWN_DURATION,
+                        ACCEL_DEACCEL);
+                float endTranslationY = endRect.top -
+                        (appBounds.height() - thumbnailView.getHeight()) / 2.0f;
+                mTranslationY = new FloatProp(0, endTranslationY, 0, APP_SCALE_DOWN_DURATION,
+                        ACCEL_DEACCEL);
+
+                // Fade out quietly near the end to be replaced by the real view.
+                mAlpha = new FloatProp(1.0f, 0,
+                        APP_SCALE_DOWN_DURATION - APP_TO_THUMBNAIL_FADE_DURATION,
+                        APP_TO_THUMBNAIL_FADE_DURATION, ACCEL_2);
+            }
+
+            @Override
+            public void onUpdate(float percent) {
+                Matrix m = new Matrix();
+                m.setScale(mScaleX.value, mScaleY.value,
+                        appBounds.width() / 2.0f, appBounds.height() / 2.0f);
+                m.postTranslate(mTranslationX.value, mTranslationY.value);
+
+                params[1] = new SurfaceParams(appTarget.leash, mAlpha.value, m,
+                        null /* windowCrop */, getLayer(appTarget, MODE_CLOSING),
+                        0 /* cornerRadius */);
+                surfaceApplier.scheduleApply(params);
+            }
+        });
+        anim.play(valueAnimator);
+    }
+
+    /**
      * Get duration of animation from app to overview.
      *
      * @return duration of animation
      */
     long getRecentsLaunchDuration() {
-        return RECENTS_LAUNCH_DURATION;
+        return APP_SCALE_DOWN_DURATION;
     }
 }
diff --git a/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java b/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
index d55d2e5..40db7dd 100644
--- a/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -21,6 +21,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherInitListener;
+import com.android.launcher3.LauncherState;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.quickstep.views.IconRecentsView;
@@ -38,10 +39,14 @@
     public AnimationFactory prepareRecentsUI(Launcher activity,
             boolean activityVisible, boolean animateActivity,
             Consumer<AnimatorPlaybackController> callback) {
+        LauncherState fromState = activity.getStateManager().getState();
         //TODO: Implement this based off where the recents view needs to be for app => recents anim.
         return new AnimationFactory() {
             @Override
-            public void createActivityController(long transitionLength) {}
+            public void createActivityController(long transitionLength) {
+                callback.accept(activity.getStateManager().createAnimationToNewWorkspace(
+                        fromState, OVERVIEW, transitionLength));
+            }
 
             @Override
             public void onTransitionCancelled() {}
diff --git a/go/quickstep/src/com/android/quickstep/TaskAdapter.java b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
index 4f3d1e4..9a2c0f8 100644
--- a/go/quickstep/src/com/android/quickstep/TaskAdapter.java
+++ b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
@@ -15,10 +15,12 @@
  */
 package com.android.quickstep;
 
+import android.util.ArrayMap;
 import android.view.LayoutInflater;
 import android.view.ViewGroup;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.recyclerview.widget.RecyclerView.Adapter;
 
 import com.android.launcher3.R;
@@ -36,6 +38,7 @@
     private static final int MAX_TASKS_TO_DISPLAY = 6;
     private static final String TAG = "TaskAdapter";
     private final TaskListLoader mLoader;
+    private final ArrayMap<Integer, TaskItemView> mTaskIdToViewMap = new ArrayMap<>();
     private TaskInputController mInputController;
 
     public TaskAdapter(@NonNull TaskListLoader loader) {
@@ -46,6 +49,16 @@
         mInputController = inputController;
     }
 
+    /**
+     * Get task item view for a given task id if it's attached to the view.
+     *
+     * @param taskId task id to search for
+     * @return corresponding task item view if it's attached, null otherwise
+     */
+    public @Nullable TaskItemView getTaskItemView(int taskId) {
+        return mTaskIdToViewMap.get(taskId);
+    }
+
     @Override
     public TaskHolder onCreateViewHolder(ViewGroup parent, int viewType) {
         TaskItemView itemView = (TaskItemView) LayoutInflater.from(parent.getContext())
@@ -63,6 +76,17 @@
             return;
         }
         holder.bindTask(tasks.get(position));
+
+    }
+
+    @Override
+    public void onViewAttachedToWindow(@NonNull TaskHolder holder) {
+        mTaskIdToViewMap.put(holder.getTask().key.id, (TaskItemView) holder.itemView);
+    }
+
+    @Override
+    public void onViewDetachedFromWindow(@NonNull TaskHolder holder) {
+        mTaskIdToViewMap.remove(holder.getTask().key.id);
     }
 
     @Override
diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
index e294282..3fdaefe 100644
--- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
+++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
@@ -27,6 +27,7 @@
 import android.widget.FrameLayout;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.recyclerview.widget.ItemTouchHelper;
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
@@ -155,6 +156,20 @@
         });
     }
 
+    /**
+     * Get the thumbnail view associated with a task for the purposes of animation.
+     *
+     * @param taskId task id of thumbnail view to get
+     * @return the thumbnail view for the task if attached, null otherwise
+     */
+    public @Nullable View getThumbnailViewForTask(int taskId) {
+        TaskItemView view = mTaskAdapter.getTaskItemView(taskId);
+        if (view == null) {
+            return null;
+        }
+        return view.getThumbnailView();
+    }
+
     public void setTranslationYFactor(float translationFactor) {
         mTranslationYFactor = translationFactor;
         setTranslationY(computeTranslationYForFactor(mTranslationYFactor));
diff --git a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
index 3818965..373f107 100644
--- a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
+++ b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
@@ -19,6 +19,7 @@
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import android.view.View;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
@@ -76,4 +77,8 @@
     public void setThumbnail(Bitmap thumbnail) {
         mThumbnailView.setImageBitmap(thumbnail);
     }
+
+    public View getThumbnailView() {
+        return mThumbnailView;
+    }
 }