diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java index afc8e47464..47e0e614f3 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 @@ package com.android.launcher3.uioverrides; 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 @@ -49,6 +47,12 @@ public class OverviewState extends LauncherState { return new float[] {1f, 0f}; } + @Override + public void onStateEnabled(Launcher launcher) { + IconRecentsView recentsView = launcher.getOverviewPanel(); + recentsView.onBeginTransitionToOverview(); + } + @Override public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) { return new PageAlphaProvider(DEACCEL_2) { diff --git a/go/quickstep/src/com/android/quickstep/TaskAdapter.java b/go/quickstep/src/com/android/quickstep/TaskAdapter.java index 77c3f33b04..57cd60a992 100644 --- a/go/quickstep/src/com/android/quickstep/TaskAdapter.java +++ b/go/quickstep/src/com/android/quickstep/TaskAdapter.java @@ -33,10 +33,10 @@ public final class TaskAdapter extends Adapter { private static final int MAX_TASKS_TO_DISPLAY = 6; private static final String TAG = "TaskAdapter"; - private final ArrayList mTaskList; + private final TaskListLoader mLoader; - public TaskAdapter(@NonNull ArrayList taskList) { - mTaskList = taskList; + public TaskAdapter(@NonNull TaskListLoader loader) { + mLoader = loader; } @Override @@ -48,11 +48,16 @@ public final class TaskAdapter extends Adapter { @Override public void onBindViewHolder(TaskHolder holder, int position) { - holder.bindTask(mTaskList.get(position)); + ArrayList 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 0000000000..9f359b4c1b --- /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 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 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> 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 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 15da10cdd9..afb05409ca 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.LinearLayoutManager; 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 @@ public final class IconRecentsView extends FrameLayout { */ @ViewDebug.ExportedProperty(category = "launcher") - // TODO: Write a recents task list observer that creates/updates tasks and signals task adapter. - private static final ArrayList 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 @@ public final class IconRecentsView extends FrameLayout { @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));