Merge "Show ASAP for Recents Go and load content after" into ub-launcher3-master

This commit is contained in:
TreeHugger Robot 2019-03-29 02:07:01 +00:00 committed by Android (Google) Code Review
commit 7c1d69e515
4 changed files with 116 additions and 69 deletions

View File

@ -75,8 +75,20 @@ public final class TaskAdapter extends Adapter<TaskHolder> {
// Task list has updated.
return;
}
holder.bindTask(tasks.get(position));
Task task = tasks.get(position);
holder.bindTask(task);
mLoader.loadTaskIconAndLabel(task, () -> {
// Ensure holder still has the same task.
if (task.equals(holder.getTask())) {
holder.getTaskItemView().setIcon(task.icon);
holder.getTaskItemView().setLabel(task.titleDescription);
}
});
mLoader.loadTaskThumbnail(task, () -> {
if (task.equals(holder.getTask())) {
holder.getTaskItemView().setThumbnail(task.thumbnail.thumbnail);
}
});
}
@Override

View File

@ -35,17 +35,18 @@ public final class TaskHolder extends ViewHolder {
mTaskItemView = itemView;
}
public TaskItemView getTaskItemView() {
return mTaskItemView;
}
/**
* Bind task content to the view. This includes the task icon and title as well as binding
* input handlers such as which task to launch/remove.
* Bind a task to the holder, resetting the view and preparing it for content to load in.
*
* @param task the task to bind to the view
*/
public void bindTask(Task task) {
mTask = task;
mTaskItemView.setLabel(task.titleDescription);
mTaskItemView.setIcon(task.icon);
mTaskItemView.setThumbnail(task.thumbnail.thumbnail);
mTaskItemView.resetTaskItemView();
}
/**

View File

@ -25,7 +25,6 @@ import com.android.systemui.shared.recents.model.Task;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
/**
@ -39,35 +38,48 @@ public final class TaskListLoader {
private ArrayList<Task> mTaskList = new ArrayList<>();
private int mTaskListChangeId;
private RecentsModel.TaskThumbnailChangeListener listener = (taskId, thumbnailData) -> {
Task foundTask = null;
for (Task task : mTaskList) {
if (task.key.id == taskId) {
foundTask = task;
break;
}
}
if (foundTask != null) {
foundTask.thumbnail = thumbnailData;
}
return foundTask;
};
public TaskListLoader(Context context) {
mRecentsModel = RecentsModel.INSTANCE.get(context);
mRecentsModel.addThumbnailChangeListener(listener);
}
/**
* Returns the current task list as of the last completed load (see
* {@link #loadTaskList}) as a read-only list. This list of tasks is guaranteed to always have
* all its task content loaded.
* Returns the current task list as of the last completed load (see {@link #loadTaskList}) as a
* read-only list. This list of tasks is not guaranteed to have all content loaded.
*
* @return the current list of tasks w/ all content loaded
* @return the current list of tasks
*/
public List<Task> getCurrentTaskList() {
return Collections.unmodifiableList(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.
* Fetches the most recent tasks and updates the task list asynchronously. This call does not
* provide guarantees the task content (icon, thumbnail, label) are loaded but will fill in
* what it has. May run the callback immediately if there have been no changes in the task
* list.
*
* @param onTasksLoadedCallback callback for when the tasks are fully loaded. Done on the UI
* thread
* @param onLoadedCallback callback to run when task list is loaded
*/
public void loadTaskList(@Nullable Consumer<ArrayList<Task>> onTasksLoadedCallback) {
public void loadTaskList(@Nullable Consumer<ArrayList<Task>> onLoadedCallback) {
if (mRecentsModel.isTaskListValid(mTaskListChangeId)) {
// Current task list is already up to date. No need to update.
if (onTasksLoadedCallback != null) {
onTasksLoadedCallback.accept(mTaskList);
if (onLoadedCallback != null) {
onLoadedCallback.accept(mTaskList);
}
return;
}
@ -76,15 +88,45 @@ public final class TaskListLoader {
// 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);
for (Task task : tasks) {
int loadedPos = mTaskList.indexOf(task);
if (loadedPos == -1) {
continue;
}
});
Task loadedTask = mTaskList.get(loadedPos);
task.icon = loadedTask.icon;
task.titleDescription = loadedTask.titleDescription;
task.thumbnail = loadedTask.thumbnail;
}
mTaskList = tasks;
onLoadedCallback.accept(tasks);
});
}
/**
* Load task icon and label asynchronously if it is not already loaded in the task. If the task
* already has an icon, this calls the callback immediately.
*
* @param task task to update with icon + label
* @param onLoadedCallback callback to run when task has icon and label
*/
public void loadTaskIconAndLabel(Task task, @Nullable Runnable onLoadedCallback) {
mRecentsModel.getIconCache().updateIconInBackground(task,
loadedTask -> onLoadedCallback.run());
}
/**
* Load thumbnail asynchronously if not already loaded in the task. If the task already has a
* thumbnail or if the thumbnail is cached, this calls the callback immediately.
*
* @param task task to update with the thumbnail
* @param onLoadedCallback callback to run when task has thumbnail
*/
public void loadTaskThumbnail(Task task, @Nullable Runnable onLoadedCallback) {
mRecentsModel.getThumbnailCache().updateThumbnailInBackground(task,
thumbnail -> onLoadedCallback.run());
}
/**
* Removes the task from the current task list.
*/
@ -98,42 +140,4 @@ public final class TaskListLoader {
void clearAllTasks() {
mTaskList.clear();
}
/**
* Loads task content for a list of tasks, including the label, icon, and thumbnail. For content
* that isn't cached, load the content asynchronously in the background.
*
* @param tasksToLoad list of tasks that need to load their content
* @param onFullyLoadedCallback runnable to run after all tasks have loaded their content
*/
private void loadTaskContents(ArrayList<Task> tasksToLoad,
@Nullable Runnable onFullyLoadedCallback) {
// Make two load requests per task, one for the icon/title and one for the thumbnail.
AtomicInteger loadRequestsCount = new AtomicInteger(tasksToLoad.size() * 2);
Runnable itemLoadedRunnable = () -> {
if (loadRequestsCount.decrementAndGet() == 0 && onFullyLoadedCallback != null) {
onFullyLoadedCallback.run();
}
};
for (Task task : tasksToLoad) {
// Load icon and title.
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;
itemLoadedRunnable.run();
} else {
// Otherwise, load the content in the background.
mRecentsModel.getIconCache().updateIconInBackground(task,
loadedTask -> itemLoadedRunnable.run());
}
// Load the thumbnail. May return immediately and synchronously if the thumbnail is
// cached.
mRecentsModel.getThumbnailCache().updateThumbnailInBackground(task,
thumbnail -> itemLoadedRunnable.run());
}
}
}

View File

@ -17,6 +17,7 @@ package com.android.quickstep.views;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
@ -24,6 +25,8 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
import com.android.launcher3.R;
/**
@ -31,12 +34,16 @@ import com.android.launcher3.R;
*/
public final class TaskItemView extends LinearLayout {
private static final String DEFAULT_LABEL = "...";
private final Drawable mDefaultIcon;
private TextView mLabelView;
private ImageView mIconView;
private ImageView mThumbnailView;
public TaskItemView(Context context, AttributeSet attrs) {
super(context, attrs);
mDefaultIcon = context.getResources().getDrawable(
android.R.drawable.sym_def_app_icon, context.getTheme());
}
@Override
@ -48,33 +55,56 @@ public final class TaskItemView extends LinearLayout {
}
/**
* Set the label for the task item.
* Resets task item view to default values.
*/
public void resetTaskItemView() {
setLabel(DEFAULT_LABEL);
setIcon(null);
setThumbnail(null);
}
/**
* Set the label for the task item. Sets to a default label if null.
*
* @param label task label
*/
public void setLabel(String label) {
public void setLabel(@Nullable String label) {
if (label == null) {
mLabelView.setText(DEFAULT_LABEL);
return;
}
mLabelView.setText(label);
}
/**
* Set the icon for the task item.
* Set the icon for the task item. Sets to a default icon if null.
*
* @param icon task icon
*/
public void setIcon(Drawable icon) {
public void setIcon(@Nullable Drawable icon) {
// TODO: Scale the icon up based off the padding on the side
// The icon proper is actually smaller than the drawable and has "padding" on the side for
// the purpose of drawing the shadow, allowing the icon to pop up, so we need to scale the
// view if we want the icon to be flush with the bottom of the thumbnail.
if (icon == null) {
mIconView.setImageDrawable(mDefaultIcon);
return;
}
mIconView.setImageDrawable(icon);
}
/**
* Set the task thumbnail for the task.
* Set the task thumbnail for the task. Sets to a default thumbnail if null.
*
* @param thumbnail task thumbnail for the task
*/
public void setThumbnail(Bitmap thumbnail) {
public void setThumbnail(@Nullable Bitmap thumbnail) {
if (thumbnail == null) {
mThumbnailView.setImageBitmap(null);
mThumbnailView.setBackgroundColor(Color.GRAY);
return;
}
mThumbnailView.setBackgroundColor(Color.TRANSPARENT);
mThumbnailView.setImageBitmap(thumbnail);
}