Move clear all to recycler view (1/2)

First part of moving clear all button to recycler view.  This CL adds
support in the recycler view adapter for a clear all holder type and
hooks it up to the previous clear all animation.

Adding this breaks several assumptions made externally on the type of
the item and index which will be addressed in the second part.

Bug: 114136250
Test: Builds, testing pending 2nd part
Change-Id: Ib16790028d4e9f520945a987b3dace40d19f2468
diff --git a/go/quickstep/res/layout/clear_all_button.xml b/go/quickstep/res/layout/clear_all_button.xml
new file mode 100644
index 0000000..eceffec
--- /dev/null
+++ b/go/quickstep/res/layout/clear_all_button.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/clear_all_button"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center_horizontal"
+    android:background="@drawable/clear_all_button"
+    android:gravity="center"
+    android:text="@string/recents_clear_all"
+    android:textAllCaps="false"
+    android:textColor="@color/clear_all_button_text"
+    android:textSize="14sp">
+</Button>
diff --git a/go/quickstep/res/layout/icon_recents_root_view.xml b/go/quickstep/res/layout/icon_recents_root_view.xml
index 6dc293f..6fb7e19 100644
--- a/go/quickstep/res/layout/icon_recents_root_view.xml
+++ b/go/quickstep/res/layout/icon_recents_root_view.xml
@@ -19,29 +19,11 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical">
-    <LinearLayout
-        android:id="@+id/recent_task_content_view"
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/recent_task_recycler_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:orientation="vertical"
-        android:visibility="gone">
-        <androidx.recyclerview.widget.RecyclerView
-            android:id="@+id/recent_task_recycler_view"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:scrollbars="none"/>
-        <Button
-            android:id="@+id/clear_all_button"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center_horizontal"
-            android:background="@drawable/clear_all_button"
-            android:gravity="center"
-            android:text="@string/recents_clear_all"
-            android:textAllCaps="false"
-            android:textColor="@color/clear_all_button_text"
-            android:textSize="14sp"/>
-    </LinearLayout>
+        android:scrollbars="none"/>
     <TextView
         android:id="@+id/recent_task_empty_view"
         android:layout_width="match_parent"
diff --git a/go/quickstep/src/com/android/quickstep/ClearAllHolder.java b/go/quickstep/src/com/android/quickstep/ClearAllHolder.java
new file mode 100644
index 0000000..ce87171
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/ClearAllHolder.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView.ViewHolder;
+
+/**
+ * Holder for clear all button view in task recycler view.
+ */
+final class ClearAllHolder extends ViewHolder {
+    public ClearAllHolder(@NonNull View itemView) {
+        super(itemView);
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/TaskAdapter.java b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
index 18d5877..4eec78b 100644
--- a/go/quickstep/src/com/android/quickstep/TaskAdapter.java
+++ b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
@@ -16,12 +16,14 @@
 package com.android.quickstep;
 
 import android.view.LayoutInflater;
+import android.view.View.OnClickListener;
 import android.view.ViewGroup;
+import android.widget.Button;
 
 import androidx.annotation.NonNull;
 import androidx.recyclerview.widget.RecyclerView.Adapter;
+import androidx.recyclerview.widget.RecyclerView.ViewHolder;
 
-import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.quickstep.views.TaskItemView;
 import com.android.systemui.shared.recents.model.Task;
@@ -34,14 +36,17 @@
  * Recycler view adapter that dynamically inflates and binds {@link TaskHolder} instances with the
  * appropriate {@link Task} from the recents task list.
  */
-public final class TaskAdapter extends Adapter<TaskHolder> {
+public final class TaskAdapter extends Adapter<ViewHolder> {
 
     public static final int CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT = 0;
     public static final int MAX_TASKS_TO_DISPLAY = 6;
 
     private static final String TAG = "TaskAdapter";
+    private static final int ITEM_TYPE_TASK = 0;
+    private static final int ITEM_TYPE_CLEAR_ALL = 1;
     private final TaskListLoader mLoader;
     private TaskActionController mTaskActionController;
+    private OnClickListener mClearAllListener;
     private boolean mIsShowingLoadingUi;
 
     public TaskAdapter(@NonNull TaskListLoader loader) {
@@ -52,6 +57,10 @@
         mTaskActionController = taskActionController;
     }
 
+    public void setOnClearAllClickListener(OnClickListener listener) {
+        mClearAllListener = listener;
+    }
+
     /**
      * Sets all positions in the task adapter to loading views, binding new views if necessary.
      * This changes the task adapter's view of the data, so the appropriate notify events should be
@@ -65,21 +74,32 @@
     }
 
     @Override
-    public TaskHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-        TaskItemView itemView = (TaskItemView) LayoutInflater.from(parent.getContext())
-                .inflate(R.layout.task_item_view, parent, false);
-        TaskHolder holder = new TaskHolder(itemView);
-        itemView.setOnClickListener(view -> mTaskActionController.launchTask(holder));
-        return holder;
+    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        switch (viewType) {
+            case ITEM_TYPE_TASK:
+                TaskItemView itemView = (TaskItemView) LayoutInflater.from(parent.getContext())
+                        .inflate(R.layout.task_item_view, parent, false);
+                TaskHolder taskHolder = new TaskHolder(itemView);
+                itemView.setOnClickListener(view -> mTaskActionController.launchTask(taskHolder));
+                return taskHolder;
+            case ITEM_TYPE_CLEAR_ALL:
+                Button clearView = (Button) LayoutInflater.from(parent.getContext())
+                        .inflate(R.layout.clear_all_button, parent, false);
+                ClearAllHolder clearAllHolder = new ClearAllHolder(clearView);
+                clearView.setOnClickListener(mClearAllListener);
+                return clearAllHolder;
+            default:
+                throw new IllegalArgumentException("No known holder for item type: " + viewType);
+        }
     }
 
     @Override
-    public void onBindViewHolder(TaskHolder holder, int position) {
+    public void onBindViewHolder(ViewHolder holder, int position) {
         onBindViewHolderInternal(holder, position, false /* willAnimate */);
     }
 
     @Override
-    public void onBindViewHolder(@NonNull TaskHolder holder, int position,
+    public void onBindViewHolder(@NonNull ViewHolder holder, int position,
             @NonNull List<Object> payloads) {
         if (payloads.isEmpty()) {
             super.onBindViewHolder(holder, position, payloads);
@@ -95,40 +115,60 @@
         }
     }
 
-    private void onBindViewHolderInternal(@NonNull TaskHolder holder, int position,
+    private void onBindViewHolderInternal(@NonNull ViewHolder holder, int position,
             boolean willAnimate) {
-        if (mIsShowingLoadingUi) {
-            holder.bindEmptyUi();
-            return;
+        int itemType = getItemViewType(position);
+        switch (itemType) {
+            case ITEM_TYPE_TASK:
+                TaskHolder taskHolder = (TaskHolder) holder;
+                if (mIsShowingLoadingUi) {
+                    taskHolder.bindEmptyUi();
+                    return;
+                }
+                List<Task> tasks = mLoader.getCurrentTaskList();
+                if (position >= tasks.size()) {
+                    // Task list has updated.
+                    return;
+                }
+                Task task = tasks.get(position);
+                taskHolder.bindTask(task, willAnimate /* willAnimate */);
+                mLoader.loadTaskIconAndLabel(task, () -> {
+                    // Ensure holder still has the same task.
+                    if (Objects.equals(Optional.of(task), taskHolder.getTask())) {
+                        taskHolder.getTaskItemView().setIcon(task.icon);
+                        taskHolder.getTaskItemView().setLabel(task.titleDescription);
+                    }
+                });
+                mLoader.loadTaskThumbnail(task, () -> {
+                    if (Objects.equals(Optional.of(task), taskHolder.getTask())) {
+                        taskHolder.getTaskItemView().setThumbnail(task.thumbnail.thumbnail);
+                    }
+                });
+                break;
+            case ITEM_TYPE_CLEAR_ALL:
+                // Nothing to bind.
+                break;
+            default:
+                throw new IllegalArgumentException("No known holder for item type: " + itemType);
         }
-        List<Task> tasks = mLoader.getCurrentTaskList();
-        if (position >= tasks.size()) {
-            // Task list has updated.
-            return;
-        }
-        Task task = tasks.get(position);
-        holder.bindTask(task, willAnimate /* willAnimate */);
-        mLoader.loadTaskIconAndLabel(task, () -> {
-            // Ensure holder still has the same task.
-            if (Objects.equals(Optional.of(task), holder.getTask())) {
-                holder.getTaskItemView().setIcon(task.icon);
-                holder.getTaskItemView().setLabel(task.titleDescription);
-            }
-        });
-        mLoader.loadTaskThumbnail(task, () -> {
-            if (Objects.equals(Optional.of(task), holder.getTask())) {
-                holder.getTaskItemView().setThumbnail(task.thumbnail.thumbnail);
-            }
-        });
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        // Bottom is always clear all button.
+        return (position == 0) ? ITEM_TYPE_CLEAR_ALL : ITEM_TYPE_TASK;
     }
 
     @Override
     public int getItemCount() {
+        // Always at least one for clear all button.
+        int itemCount = 1;
         if (mIsShowingLoadingUi) {
             // Show loading version of all items.
-            return MAX_TASKS_TO_DISPLAY;
+            itemCount += MAX_TASKS_TO_DISPLAY;
         } else {
-            return Math.min(mLoader.getCurrentTaskList().size(), MAX_TASKS_TO_DISPLAY);
+            itemCount += Math.min(mLoader.getCurrentTaskList().size(), MAX_TASKS_TO_DISPLAY);
         }
+        return itemCount;
     }
 }
diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
index 3c08b7e..e2dad58 100644
--- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
+++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
@@ -18,9 +18,6 @@
 import static androidx.recyclerview.widget.LinearLayoutManager.VERTICAL;
 
 import static com.android.quickstep.TaskAdapter.CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT;
-import static com.android.quickstep.views.TaskLayoutUtils.getClearAllButtonHeight;
-import static com.android.quickstep.views.TaskLayoutUtils.getClearAllButtonTopBottomMargin;
-import static com.android.quickstep.views.TaskLayoutUtils.getClearAllButtonWidth;
 import static com.android.quickstep.views.TaskLayoutUtils.getTaskListHeight;
 
 import android.animation.Animator;
@@ -114,7 +111,6 @@
     private View mShowingContentView;
     private View mEmptyView;
     private View mContentView;
-    private View mClearAllView;
     private boolean mTransitionedFromApp;
     private AnimatorSet mLayoutAnimation;
     private final ArraySet<View> mLayingOutViews = new ArraySet<>();
@@ -141,6 +137,7 @@
         mDeviceProfile = activity.getDeviceProfile();
         mTaskLoader = new TaskListLoader(mContext);
         mTaskAdapter = new TaskAdapter(mTaskLoader);
+        mTaskAdapter.setOnClearAllClickListener(view -> animateClearAllTasks());
         mTaskActionController = new TaskActionController(mTaskLoader, mTaskAdapter);
         mTaskAdapter.setActionController(mTaskActionController);
         RecentsModel.INSTANCE.get(context).addThumbnailChangeListener(listener);
@@ -178,7 +175,7 @@
                     () -> mTaskRecyclerView.setItemAnimator(new DefaultItemAnimator()));
 
             mEmptyView = findViewById(R.id.recent_task_empty_view);
-            mContentView = findViewById(R.id.recent_task_content_view);
+            mContentView = mTaskRecyclerView;
             mTaskAdapter.registerAdapterDataObserver(new AdapterDataObserver() {
                 @Override
                 public void onChanged() {
@@ -190,16 +187,7 @@
                     updateContentViewVisibility();
                 }
             });
-            // TODO: Move clear all button to recycler view so that it can scroll off screen.
             // TODO: Move layout param logic into onMeasure
-            mClearAllView = findViewById(R.id.clear_all_button);
-            MarginLayoutParams clearAllParams =
-                    (MarginLayoutParams) mClearAllView.getLayoutParams();
-            clearAllParams.height = getClearAllButtonHeight(mDeviceProfile);
-            clearAllParams.width = getClearAllButtonWidth(mDeviceProfile);
-            clearAllParams.topMargin = getClearAllButtonTopBottomMargin(mDeviceProfile);
-            clearAllParams.bottomMargin = getClearAllButtonTopBottomMargin(mDeviceProfile);
-            mClearAllView.setOnClickListener(v -> animateClearAllTasks());
         }
     }
 
@@ -210,7 +198,7 @@
         for (TaskItemView itemView : itemViews) {
             itemView.setEnabled(enabled);
         }
-        mClearAllView.setEnabled(enabled);
+        // TODO: Disable clear all button.
     }
 
     /**
@@ -365,6 +353,7 @@
      * @return array of attached task item views
      */
     private TaskItemView[] getTaskViews() {
+        // TODO: Check that clear all button isn't here..
         int taskCount = mTaskRecyclerView.getChildCount();
         TaskItemView[] itemViews = new TaskItemView[taskCount];
         for (int i = 0; i < taskCount; i ++) {