Create dynamic resource for GTS RecyclerView

The GridTabSwitcher RecyclerView would be rendered to a Bitmap
as the background in TabListSceneLayer, so that the shrinking Tab
can be animated on top of the (Bitmap version of) GTS RecyclerView.

Bug: 964406
Change-Id: I9962cbcce42ad7787e170a2fd855375a318cf324
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1617892
Commit-Queue: Wei-Yin Chen (陳威尹) <wychen@chromium.org>
Reviewed-by: Yusuf Ozuysal <yusufo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#662498}
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcher.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcher.java
index c50daee..96ba5bd 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcher.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcher.java
@@ -24,4 +24,9 @@
     interface GridVisibilityObserver extends OverviewModeBehavior.OverviewModeObserver {}
 
     GridController getGridController();
+
+    /**
+     * @return The dynamic resource ID of the GridTabSwitcher RecyclerView.
+     */
+    int getResourceId();
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java
index 8bc9790..589b39a 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java
@@ -81,7 +81,7 @@
         mTabGridCoordinator = new TabListCoordinator(TabListCoordinator.TabListMode.GRID, context,
                 tabModelSelector, mMultiThumbnailCardProvider, titleProvider, true,
                 mMediator::getCreateGroupButtonOnClickListener, gridCardOnClickListenerProvider,
-                compositorViewHolder, true,
+                compositorViewHolder, compositorViewHolder.getDynamicResourceLoader(), true,
                 org.chromium.chrome.tab_ui.R.layout.grid_tab_switcher_layout, COMPONENT_NAME);
         HistoryNavigationLayout navigation =
                 compositorViewHolder.findViewById(R.id.history_navigation);
@@ -103,6 +103,11 @@
         return mMediator;
     }
 
+    @Override
+    public int getResourceId() {
+        return mTabGridCoordinator.getResourceId();
+    }
+
     /**
      * Reset the tab grid with the given {@link TabModel}. Can be null.
      * @param tabList The current {@link TabList} to show the tabs for in the grid.
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
index cf0751e..ab644b2 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
@@ -41,7 +41,7 @@
 
         mTabListCoordinator = new TabListCoordinator(TabListCoordinator.TabListMode.GRID, context,
                 tabModelSelector, tabContentManager::getTabThumbnailWithCallback, null, false, null,
-                null, compositorViewHolder, false, R.layout.tab_list_recycler_view_layout,
+                null, compositorViewHolder, null, false, R.layout.tab_list_recycler_view_layout,
                 COMPONENT_NAME);
 
         mMediator = new TabGridDialogMediator(context, this::resetWithListOfTabs,
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetCoordinator.java
index e9ebf9e..219050e 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetCoordinator.java
@@ -45,7 +45,7 @@
 
         mTabGridCoordinator = new TabListCoordinator(TabListCoordinator.TabListMode.GRID, context,
                 tabModelSelector, tabContentManager::getTabThumbnailWithCallback, null, false, null,
-                null, bottomSheetController.getBottomSheet(), false,
+                null, bottomSheetController.getBottomSheet(), null, false,
                 R.layout.tab_list_recycler_view_layout, COMPONENT_NAME);
 
         mMediator = new TabGridSheetMediator(mContext, bottomSheetController,
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java
index 04371c4..2ab80e8 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java
@@ -76,7 +76,7 @@
 
         mTabStripCoordinator = new TabListCoordinator(TabListCoordinator.TabListMode.STRIP,
                 mContext, tabModelSelector, null, null, false, null, null,
-                mTabStripToolbarCoordinator.getTabListContainerView(), true,
+                mTabStripToolbarCoordinator.getTabListContainerView(), null, true,
                 R.layout.tab_list_recycler_view_layout, COMPONENT_NAME);
 
         mTabGridSheetCoordinator =
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
index 892ea19..2781f53 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
@@ -27,6 +27,7 @@
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.RecyclerViewAdapter;
 import org.chromium.ui.modelutil.SimpleRecyclerViewMcpBase;
+import org.chromium.ui.resources.dynamics.DynamicResourceLoader;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -63,6 +64,8 @@
      * @param createGroupButtonProvider {@link TabListMediator.CreateGroupButtonProvider}
      *         to provide "Create group" button.
      * @param parentView {@link ViewGroup} The root view of the UI.
+     * @param dynamicResourceLoader The {@link DynamicResourceLoader} to register dynamic UI
+     *                              resource for compositor layer animation.
      * @param attachToParent Whether the UI should attach to root view.
      * @param layoutId ID of the layout resource.
      * @param componentName A unique string uses to identify different components for UMA recording.
@@ -70,13 +73,13 @@
      *                      through actions.xml file.
      */
     TabListCoordinator(@TabListMode int mode, Context context, TabModelSelector tabModelSelector,
-            TabListMediator.ThumbnailProvider thumbnailProvider,
-            TabListMediator.TitleProvider titleProvider, boolean closeRelatedTabs,
+            @Nullable TabListMediator.ThumbnailProvider thumbnailProvider,
+            @Nullable TabListMediator.TitleProvider titleProvider, boolean closeRelatedTabs,
             @Nullable TabListMediator.CreateGroupButtonProvider createGroupButtonProvider,
             @Nullable TabListMediator
                     .GridCardOnClickListenerProvider gridCardOnClickListenerProvider,
-            @NonNull ViewGroup parentView, boolean attachToParent, @LayoutRes int layoutId,
-            String componentName) {
+            @NonNull ViewGroup parentView, @Nullable DynamicResourceLoader dynamicResourceLoader,
+            boolean attachToParent, @LayoutRes int layoutId, String componentName) {
         TabListModel tabListModel = new TabListModel();
         mMode = mode;
 
@@ -125,6 +128,10 @@
 
         mRecyclerView.setHasFixedSize(true);
 
+        if (dynamicResourceLoader != null) {
+            mRecyclerView.createDynamicView(dynamicResourceLoader);
+        }
+
         TabListFaviconProvider tabListFaviconProvider =
                 new TabListFaviconProvider(context, Profile.getLastUsedProfile());
 
@@ -169,4 +176,8 @@
     public void destroy() {
         mMediator.destroy();
     }
+
+    int getResourceId() {
+        return mRecyclerView.getResourceId();
+    }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java
index a345f98..aead8b6 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java
@@ -8,18 +8,24 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
+import android.annotation.SuppressLint;
 import android.content.Context;
+import android.graphics.Rect;
 import android.support.v7.widget.RecyclerView;
 import android.util.AttributeSet;
 import android.view.View;
+import android.view.ViewParent;
 
 import org.chromium.ui.interpolators.BakedBezierInterpolator;
+import org.chromium.ui.resources.dynamics.DynamicResourceLoader;
+import org.chromium.ui.resources.dynamics.ViewResourceAdapter;
 
 /**
  * A custom RecyclerView implementation for the tab grid, to handle show/hide logic in class.
  */
 class TabListRecyclerView extends RecyclerView {
     public static final long BASE_ANIMATION_DURATION_MS = 218;
+    private ViewResourceAdapter mDynamicView;
 
     /**
      * An interface to listen to visibility related changes on this {@link RecyclerView}.
@@ -84,12 +90,49 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 mListener.finishedShowing();
+
+                if (mDynamicView != null)
+                    mDynamicView.dropCachedBitmap();
             }
         });
         if (!animate) mFadeInAnimator.end();
     }
 
     /**
+     * @return The ID for registering and using the dynamic resource in compositor.
+     */
+    int getResourceId() {
+        return getId();
+    }
+
+    /**
+     * Create a DynamicResource for this RecyclerView.
+     * The view resource can be obtained by {@link #getResourceId} in compositor layer.
+     */
+    void createDynamicView(DynamicResourceLoader loader) {
+        mDynamicView = new ViewResourceAdapter(this);
+        loader.registerResource(getResourceId(), mDynamicView);
+    }
+
+    @SuppressLint("NewApi") // Used on O+, invalidateChildInParent used for previous versions.
+    @Override
+    public void onDescendantInvalidated(View child, View target) {
+        super.onDescendantInvalidated(child, target);
+        if (mDynamicView != null) {
+            mDynamicView.invalidate(null);
+        }
+    }
+
+    @Override
+    public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
+        ViewParent retVal = super.invalidateChildInParent(location, dirty);
+        if (mDynamicView != null) {
+            mDynamicView.invalidate(dirty);
+        }
+        return retVal;
+    }
+
+    /**
      * Start hiding the tab list.
      * @param animate Whether the visibility change should be animated.
      */