Merge "Add loader for recents Go." into ub-launcher3-master
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
index afc8e47..47e0e61 100644
--- a/go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -19,14 +19,12 @@
 import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
 
-import android.graphics.Rect;
-
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.quickstep.RecentsModel;
+import com.android.quickstep.views.IconRecentsView;
 
 /**
  * Definition for overview state
@@ -50,6 +48,12 @@
     }
 
     @Override
+    public void onStateEnabled(Launcher launcher) {
+        IconRecentsView recentsView = launcher.getOverviewPanel();
+        recentsView.onBeginTransitionToOverview();
+    }
+
+    @Override
     public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
         return new PageAlphaProvider(DEACCEL_2) {
             @Override
diff --git a/go/quickstep/src/com/android/quickstep/TaskAdapter.java b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
index 77c3f33..57cd60a 100644
--- a/go/quickstep/src/com/android/quickstep/TaskAdapter.java
+++ b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
@@ -33,10 +33,10 @@
 
     private static final int MAX_TASKS_TO_DISPLAY = 6;
     private static final String TAG = "TaskAdapter";
-    private final ArrayList<Task> mTaskList;
+    private final TaskListLoader mLoader;
 
-    public TaskAdapter(@NonNull ArrayList<Task> taskList) {
-        mTaskList = taskList;
+    public TaskAdapter(@NonNull TaskListLoader loader) {
+        mLoader = loader;
     }
 
     @Override
@@ -48,11 +48,16 @@
 
     @Override
     public void onBindViewHolder(TaskHolder holder, int position) {
-        holder.bindTask(mTaskList.get(position));
+        ArrayList<Task> tasks = mLoader.getCurrentTaskList();
+        if (position >= tasks.size()) {
+            // Task list has updated.
+            return;
+        }
+        holder.bindTask(tasks.get(position));
     }
 
     @Override
     public int getItemCount() {
-        return Math.min(mTaskList.size(), MAX_TASKS_TO_DISPLAY);
+        return Math.min(mLoader.getCurrentTaskList().size(), MAX_TASKS_TO_DISPLAY);
     }
 }
diff --git a/go/quickstep/src/com/android/quickstep/TaskListLoader.java b/go/quickstep/src/com/android/quickstep/TaskListLoader.java
new file mode 100644
index 0000000..9f359b4
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/TaskListLoader.java
@@ -0,0 +1,118 @@
+/*
+ * 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.content.Context;
+
+import androidx.annotation.Nullable;
+
+import com.android.systemui.shared.recents.model.Task;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+
+/**
+ * This class is responsible for maintaining the list of tasks and the task content. The list must
+ * be updated explicitly with {@link #loadTaskList} whenever the list needs to be
+ * up-to-date.
+ */
+public final class TaskListLoader {
+
+    private final RecentsModel mRecentsModel;
+
+    private ArrayList<Task> mTaskList = new ArrayList<>();
+    private int mTaskListChangeId;
+
+    public TaskListLoader(Context context) {
+        mRecentsModel = RecentsModel.INSTANCE.get(context);
+    }
+
+    /**
+     * Returns the current task list as of the last completed load (see
+     * {@link #loadTaskList}). This list of tasks is guaranteed to always have all its task
+     * content loaded.
+     *
+     * @return the current list of tasks w/ all content loaded
+     */
+    public ArrayList<Task> getCurrentTaskList() {
+        return mTaskList;
+    }
+
+    /**
+     * Fetches the most recent tasks and updates the task list asynchronously. In addition it
+     * loads the content for each task (icon and label). The callback and task list being updated
+     * only occur when all task content is fully loaded and up-to-date.
+     *
+     * @param onTasksLoadedCallback callback for when the tasks are fully loaded. Done on the UI
+     *                              thread
+     */
+    public void loadTaskList(@Nullable Consumer<ArrayList<Task>> onTasksLoadedCallback) {
+        if (mRecentsModel.isTaskListValid(mTaskListChangeId)) {
+            // Current task list is already up to date. No need to update.
+            if (onTasksLoadedCallback != null) {
+                onTasksLoadedCallback.accept(mTaskList);
+            }
+            return;
+        }
+        // TODO: Look into error checking / more robust handling for when things go wrong.
+        mTaskListChangeId = mRecentsModel.getTasks(tasks -> {
+            // Reverse tasks to put most recent at the bottom of the view
+            Collections.reverse(tasks);
+            // Load task content
+            loadTaskContents(tasks, () -> {
+                mTaskList = tasks;
+                if (onTasksLoadedCallback != null) {
+                    onTasksLoadedCallback.accept(mTaskList);
+                }
+            });
+        });
+    }
+
+    /**
+     * Loads task content for a list of tasks, including the label and the icon. Uses the list of
+     * tasks since the last load as a cache for loaded content.
+     *
+     * @param tasksToLoad list of tasks that need to load their content
+     * @param onLoadedCallback runnable to run after all tasks have loaded their content
+     */
+    private void loadTaskContents(ArrayList<Task> tasksToLoad,
+            @Nullable Runnable onLoadedCallback) {
+        AtomicInteger loadRequestsCount = new AtomicInteger(0);
+        for (Task task : tasksToLoad) {
+            int index = mTaskList.indexOf(task);
+            if (index >= 0) {
+                // If we've already loaded the task and have its content then just copy it over.
+                Task loadedTask = mTaskList.get(index);
+                task.titleDescription = loadedTask.titleDescription;
+                task.icon = loadedTask.icon;
+            } else {
+                // Otherwise, load the content in the background.
+                loadRequestsCount.getAndIncrement();
+                mRecentsModel.getIconCache().updateIconInBackground(task, loadedTask -> {
+                    if (loadRequestsCount.decrementAndGet() == 0 && onLoadedCallback != null) {
+                        onLoadedCallback.run();
+                    }
+                });
+            }
+        }
+        if (loadRequestsCount.get() == 0 && onLoadedCallback != null) {
+            onLoadedCallback.run();
+        }
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
index 15da10c..afb0540 100644
--- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
+++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
@@ -27,11 +27,8 @@
 import androidx.recyclerview.widget.RecyclerView;
 
 import com.android.launcher3.R;
-import com.android.quickstep.RecentsModel;
 import com.android.quickstep.TaskAdapter;
-import com.android.systemui.shared.recents.model.Task;
-
-import java.util.ArrayList;
+import com.android.quickstep.TaskListLoader;
 
 /**
  * Root view for the icon recents view. Acts as the main interface to the rest of the Launcher code
@@ -77,13 +74,12 @@
      */
     @ViewDebug.ExportedProperty(category = "launcher")
 
-    // TODO: Write a recents task list observer that creates/updates tasks and signals task adapter.
-    private static final ArrayList<Task> DUMMY_TASK_LIST = new ArrayList<>();
     private final Context mContext;
 
     private float mTranslationYFactor;
     private TaskAdapter mTaskAdapter;
     private RecyclerView mTaskRecyclerView;
+    private TaskListLoader mTaskLoader;
 
     public IconRecentsView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -93,13 +89,30 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mTaskAdapter = new TaskAdapter(DUMMY_TASK_LIST);
+        mTaskLoader = new TaskListLoader(mContext);
+        mTaskAdapter = new TaskAdapter(mTaskLoader);
         mTaskRecyclerView = findViewById(R.id.recent_task_recycler_view);
         mTaskRecyclerView.setAdapter(mTaskAdapter);
         mTaskRecyclerView.setLayoutManager(
                 new LinearLayoutManager(mContext, VERTICAL, true /* reverseLayout */));
     }
 
+    /**
+     * Logic for when we know we are going to overview/recents and will be putting up the recents
+     * view. This should be used to prepare recents (e.g. load any task data, etc.) before it
+     * becomes visible.
+     *
+     * TODO: Hook this up for fallback recents activity as well
+     */
+    public void onBeginTransitionToOverview() {
+        // Load any task changes
+        mTaskLoader.loadTaskList(tasks -> {
+            // TODO: Put up some loading UI while task content is loading. May have to do something
+            // smarter when animating from app to overview.
+            mTaskAdapter.notifyDataSetChanged();
+        });
+    }
+
     public void setTranslationYFactor(float translationFactor) {
         mTranslationYFactor = translationFactor;
         setTranslationY(computeTranslationYForFactor(mTranslationYFactor));