Merge "Add task content animation property" into ub-launcher3-master
diff --git a/go/quickstep/src/com/android/quickstep/TouchInteractionService.java b/go/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 734425e..39f8448 100644
--- a/go/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/go/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -88,6 +88,10 @@
                 boolean gestureSwipeLeft) {
         }
 
+        public void onSystemUiStateChanged(int stateFlags) {
+            // To be implemented
+        }
+
         /** Deprecated methods **/
         public void onQuickStep(MotionEvent motionEvent) { }
 
diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
index c06b6ec..7f77b6c 100644
--- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
+++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
@@ -24,14 +24,12 @@
 import android.animation.PropertyValuesHolder;
 import android.animation.ValueAnimator;
 import android.content.Context;
+import android.util.ArraySet;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.view.View;
 import android.view.ViewDebug;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.Animation;
-import android.view.animation.AnimationSet;
-import android.view.animation.LayoutAnimationController;
+import android.view.ViewTreeObserver;
 import android.widget.FrameLayout;
 
 import androidx.annotation.NonNull;
@@ -40,6 +38,7 @@
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver;
+import androidx.recyclerview.widget.RecyclerView.OnChildAttachStateChangeListener;
 
 import com.android.launcher3.R;
 import com.android.quickstep.RecentsToActivityHelper;
@@ -90,7 +89,6 @@
     private final TaskListLoader mTaskLoader;
     private final TaskAdapter mTaskAdapter;
     private final TaskActionController mTaskActionController;
-    private final LayoutAnimationController mLayoutAnimation;
 
     private RecentsToActivityHelper mActivityHelper;
     private RecyclerView mTaskRecyclerView;
@@ -98,6 +96,9 @@
     private View mContentView;
     private View mClearAllView;
     private boolean mTransitionedFromApp;
+    private AnimatorSet mLayoutAnimation;
+    private final ArraySet<View> mLayingOutViews = new ArraySet<>();
+
 
     public IconRecentsView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -106,7 +107,6 @@
         mTaskAdapter = new TaskAdapter(mTaskLoader);
         mTaskActionController = new TaskActionController(mTaskLoader, mTaskAdapter);
         mTaskAdapter.setActionController(mTaskActionController);
-        mLayoutAnimation = createLayoutAnimation();
     }
 
     @Override
@@ -120,7 +120,20 @@
             ItemTouchHelper helper = new ItemTouchHelper(
                     new TaskSwipeCallback(mTaskActionController));
             helper.attachToRecyclerView(mTaskRecyclerView);
-            mTaskRecyclerView.setLayoutAnimation(mLayoutAnimation);
+            mTaskRecyclerView.addOnChildAttachStateChangeListener(
+                    new OnChildAttachStateChangeListener() {
+                        @Override
+                        public void onChildViewAttachedToWindow(@NonNull View view) {
+                            if (mLayoutAnimation != null && !mLayingOutViews.contains(view)) {
+                                // Child view was added that is not part of current layout animation
+                                // so restart the animation.
+                                animateFadeInLayoutAnimation();
+                            }
+                        }
+
+                        @Override
+                        public void onChildViewDetachedFromWindow(@NonNull View view) { }
+                    });
 
             mEmptyView = findViewById(R.id.recent_task_empty_view);
             mContentView = findViewById(R.id.recent_task_content_view);
@@ -165,8 +178,7 @@
      * becomes visible.
      */
     public void onBeginTransitionToOverview() {
-        mTaskRecyclerView.scheduleLayoutAnimation();
-
+        scheduleFadeInLayoutAnimation();
         // Load any task changes
         if (!mTaskLoader.needsToLoad()) {
             return;
@@ -338,17 +350,56 @@
                 });
     }
 
-    private static LayoutAnimationController createLayoutAnimation() {
-        AnimationSet anim = new AnimationSet(false /* shareInterpolator */);
+    /**
+     * Schedule a one-shot layout animation on the next layout. Separate from
+     * {@link #scheduleLayoutAnimation()} as the animation is {@link Animator} based and acts on the
+     * view properties themselves, allowing more controllable behavior and making it easier to
+     * manage when the animation conflicts with another animation.
+     */
+    private void scheduleFadeInLayoutAnimation() {
+        ViewTreeObserver viewTreeObserver = mTaskRecyclerView.getViewTreeObserver();
+        viewTreeObserver.addOnGlobalLayoutListener(
+                new ViewTreeObserver.OnGlobalLayoutListener() {
+                    @Override
+                    public void onGlobalLayout() {
+                        animateFadeInLayoutAnimation();
+                        viewTreeObserver.removeOnGlobalLayoutListener(this);
+                    }
+                });
+    }
 
-        Animation alphaAnim = new AlphaAnimation(0, 1);
-        alphaAnim.setDuration(LAYOUT_ITEM_ANIMATE_IN_DURATION);
-        anim.addAnimation(alphaAnim);
-
-        LayoutAnimationController layoutAnim = new LayoutAnimationController(anim);
-        layoutAnim.setDelay(
-                (float) LAYOUT_ITEM_ANIMATE_IN_DELAY_BETWEEN / LAYOUT_ITEM_ANIMATE_IN_DURATION);
-
-        return layoutAnim;
+    /**
+     * Start animating the layout animation where items fade in.
+     */
+    private void animateFadeInLayoutAnimation() {
+        if (mLayoutAnimation != null) {
+            // If layout animation still in progress, cancel and restart.
+            mLayoutAnimation.cancel();
+        }
+        TaskItemView[] views = getTaskViews();
+        int delay = 0;
+        mLayoutAnimation = new AnimatorSet();
+        for (TaskItemView view : views) {
+            view.setAlpha(0.0f);
+            Animator alphaAnim = ObjectAnimator.ofFloat(view, ALPHA, 0.0f, 1.0f);
+            alphaAnim.setDuration(LAYOUT_ITEM_ANIMATE_IN_DURATION).setStartDelay(delay);
+            alphaAnim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    view.setAlpha(1.0f);
+                    mLayingOutViews.remove(view);
+                }
+            });
+            delay += LAYOUT_ITEM_ANIMATE_IN_DELAY_BETWEEN;
+            mLayoutAnimation.play(alphaAnim);
+            mLayingOutViews.add(view);
+        }
+        mLayoutAnimation.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mLayoutAnimation = null;
+            }
+        });
+        mLayoutAnimation.start();
     }
 }
diff --git a/go/quickstep/src/com/android/quickstep/views/TaskLayerDrawable.java b/go/quickstep/src/com/android/quickstep/views/TaskLayerDrawable.java
index 3a23048..98b66b9 100644
--- a/go/quickstep/src/com/android/quickstep/views/TaskLayerDrawable.java
+++ b/go/quickstep/src/com/android/quickstep/views/TaskLayerDrawable.java
@@ -31,6 +31,7 @@
  */
 public final class TaskLayerDrawable extends LayerDrawable {
     private final Drawable mEmptyDrawable;
+    private float mProgress;
 
     public TaskLayerDrawable(Context context) {
         super(new Drawable[0]);
@@ -50,6 +51,7 @@
      */
     public void setCurrentDrawable(@NonNull Drawable drawable) {
         setDrawable(0, drawable);
+        applyTransitionProgress(mProgress);
     }
 
     /**
@@ -82,9 +84,18 @@
         if (progress > 1 || progress < 0) {
             throw new IllegalArgumentException("Transition progress should be between 0 and 1");
         }
+        mProgress = progress;
+        applyTransitionProgress(progress);
+    }
+
+    private void applyTransitionProgress(float progress) {
         int drawableAlpha = (int) (progress * 255);
         getDrawable(0).setAlpha(drawableAlpha);
-        getDrawable(1).setAlpha(255 - drawableAlpha);
+        if (getDrawable(0) != getDrawable(1)) {
+            // Only do this if it's a different drawable so that it fades out.
+            // Otherwise, we'd just be overwriting the front drawable's alpha.
+            getDrawable(1).setAlpha(255 - drawableAlpha);
+        }
         invalidateSelf();
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index 3ea8de0..e591177 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -159,6 +159,10 @@
                     isButton, gestureSwipeLeft, activityControl.getContainerType());
         }
 
+        public void onSystemUiStateChanged(int stateFlags) {
+            // To be implemented
+        }
+
         /** Deprecated methods **/
         public void onQuickStep(MotionEvent motionEvent) { }
 
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index c1727cc..dfb0edf 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -321,7 +321,7 @@
         final AppIconMenuItem menuItem = menu.getMenuItem(1);
         final String itemName = menuItem.getText();
 
-        menuItem.launch(getAppPackageName(), APP_NAME);
+        menuItem.launch(getAppPackageName());
     }
 
     @Test
@@ -337,7 +337,7 @@
                     getAppIcon(APP_NAME).
                     dragToWorkspace().
                     getWorkspaceAppIcon(APP_NAME).
-                    launch(getAppPackageName(), APP_NAME);
+                    launch(getAppPackageName());
         } finally {
             TestProtocol.sDebugTracing = false;
         }
@@ -365,7 +365,7 @@
         menuItem.
                 dragToWorkspace().
                 getWorkspaceAppIcon(shortcutName).
-                launch(getAppPackageName(), APP_NAME);
+                launch(getAppPackageName());
     }
 
     private static String getAppPackageName() {
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
index fbeb3a2..44fc3f7 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -50,4 +50,9 @@
                 downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, iconCenter);
         return new AppIconMenu(mLauncher, deepShortcutsContainer);
     }
+
+    @Override
+    protected String getLongPressIndicator() {
+        return "deep_shortcuts_container";
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java b/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
index c39f8d1..ba9c10e 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
@@ -32,4 +32,9 @@
     public String getText() {
         return mObject.getText();
     }
+
+    @Override
+    protected String getLongPressIndicator() {
+        return "drop_target_bar";
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java
index 16ddba8..1b372ec 100644
--- a/tests/tapl/com/android/launcher3/tapl/Launchable.java
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -27,7 +27,7 @@
 /**
  * Ancestor for AppIcon and AppMenuItem.
  */
-class Launchable {
+abstract class Launchable {
     protected final LauncherInstrumentation mLauncher;
 
     protected final UiObject2 mObject;
@@ -45,24 +45,17 @@
      * Clicks the object to launch its app.
      */
     public Background launch(String expectedPackageName) {
-        return launch(expectedPackageName, By.pkg(expectedPackageName).depth(0));
+        return launch(By.pkg(expectedPackageName));
     }
 
-    /**
-     * Clicks the object to launch its app.
-     */
-    public Background launch(String expectedPackageName, String expectedAppText) {
-        return launch(expectedPackageName, By.pkg(expectedPackageName).text(expectedAppText));
-    }
-
-    private Background launch(String errorMessage, BySelector selector) {
+    private Background launch(BySelector selector) {
         LauncherInstrumentation.log("Launchable.launch before click " +
                 mObject.getVisibleCenter());
         mLauncher.assertTrue(
                 "Launching an app didn't open a new window: " + mObject.getText(),
                 mObject.clickAndWait(Until.newWindow(), LauncherInstrumentation.WAIT_TIME_MS));
         mLauncher.assertTrue(
-                "App didn't start: " + errorMessage,
+                "App didn't start: " + selector,
                 mLauncher.getDevice().wait(Until.hasObject(selector),
                         LauncherInstrumentation.WAIT_TIME_MS));
         return new Background(mLauncher);
@@ -76,10 +69,13 @@
         Workspace.dragIconToWorkspace(
                 mLauncher,
                 this,
-                new Point(device.getDisplayWidth() / 2, device.getDisplayHeight() / 2));
+                new Point(device.getDisplayWidth() / 2, device.getDisplayHeight() / 2),
+                getLongPressIndicator());
         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                 "dragged launchable to workspace")) {
             return new Workspace(mLauncher);
         }
     }
+
+    protected abstract String getLongPressIndicator();
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 3a45e93..08f2681 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -201,13 +201,19 @@
 
     Closable addContextLayer(String piece) {
         mDiagnosticContext.addLast(piece);
-        return () -> mDiagnosticContext.removeLast();
+        log("Added context: " + getContextDescription());
+        return () -> {
+            log("Removing context: " + getContextDescription());
+            mDiagnosticContext.removeLast();
+        };
     }
 
     private void fail(String message) {
-        final String ctxt = mDiagnosticContext.isEmpty() ? "" : String.join(", ",
-                mDiagnosticContext) + "; ";
-        Assert.fail("http://go/tapl : " + ctxt + message);
+        Assert.fail("http://go/tapl : " + getContextDescription() + message);
+    }
+
+    private String getContextDescription() {
+        return mDiagnosticContext.isEmpty() ? "" : String.join(", ", mDiagnosticContext) + "; ";
     }
 
     void assertTrue(String message, boolean condition) {
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 943332e..46562ce 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -118,7 +118,7 @@
                     mLauncher,
                     getHotseatAppIcon("Chrome"),
                     new Point(mLauncher.getDevice().getDisplayWidth(),
-                            workspace.getVisibleBounds().centerY()));
+                            workspace.getVisibleBounds().centerY()), "deep_shortcuts_container");
             verifyActiveContainer();
         }
         assertTrue("Home screen workspace didn't become scrollable",
@@ -136,12 +136,13 @@
     }
 
     static void dragIconToWorkspace(
-            LauncherInstrumentation launcher, Launchable launchable, Point dest) {
+            LauncherInstrumentation launcher, Launchable launchable, Point dest,
+            String longPressIndicator) {
         LauncherInstrumentation.log("dragIconToWorkspace: begin");
         final Point launchableCenter = launchable.getObject().getVisibleCenter();
         final long downTime = SystemClock.uptimeMillis();
         launcher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, launchableCenter);
-        launcher.waitForLauncherObject("deep_shortcuts_container");
+        launcher.waitForLauncherObject(longPressIndicator);
         launcher.movePointer(downTime, DRAG_DURACTION, launchableCenter, dest);
         launcher.sendPointer(
                 downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, dest);