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. // Task list has updated.
return; 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 @Override

View File

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

View File

@ -25,7 +25,6 @@ import com.android.systemui.shared.recents.model.Task;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer; import java.util.function.Consumer;
/** /**
@ -39,35 +38,48 @@ public final class TaskListLoader {
private ArrayList<Task> mTaskList = new ArrayList<>(); private ArrayList<Task> mTaskList = new ArrayList<>();
private int mTaskListChangeId; 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) { public TaskListLoader(Context context) {
mRecentsModel = RecentsModel.INSTANCE.get(context); mRecentsModel = RecentsModel.INSTANCE.get(context);
mRecentsModel.addThumbnailChangeListener(listener);
} }
/** /**
* Returns the current task list as of the last completed load (see * Returns the current task list as of the last completed load (see {@link #loadTaskList}) as a
* {@link #loadTaskList}) as a read-only list. This list of tasks is guaranteed to always have * read-only list. This list of tasks is not guaranteed to have all content loaded.
* all its task content loaded.
* *
* @return the current list of tasks w/ all content loaded * @return the current list of tasks
*/ */
public List<Task> getCurrentTaskList() { public List<Task> getCurrentTaskList() {
return Collections.unmodifiableList(mTaskList); return Collections.unmodifiableList(mTaskList);
} }
/** /**
* Fetches the most recent tasks and updates the task list asynchronously. In addition it * Fetches the most recent tasks and updates the task list asynchronously. This call does not
* loads the content for each task (icon and label). The callback and task list being updated * provide guarantees the task content (icon, thumbnail, label) are loaded but will fill in
* only occur when all task content is fully loaded and up-to-date. * 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 * @param onLoadedCallback callback to run when task list is loaded
* thread
*/ */
public void loadTaskList(@Nullable Consumer<ArrayList<Task>> onTasksLoadedCallback) { public void loadTaskList(@Nullable Consumer<ArrayList<Task>> onLoadedCallback) {
if (mRecentsModel.isTaskListValid(mTaskListChangeId)) { if (mRecentsModel.isTaskListValid(mTaskListChangeId)) {
// Current task list is already up to date. No need to update. // Current task list is already up to date. No need to update.
if (onTasksLoadedCallback != null) { if (onLoadedCallback != null) {
onTasksLoadedCallback.accept(mTaskList); onLoadedCallback.accept(mTaskList);
} }
return; return;
} }
@ -76,15 +88,45 @@ public final class TaskListLoader {
// Reverse tasks to put most recent at the bottom of the view // Reverse tasks to put most recent at the bottom of the view
Collections.reverse(tasks); Collections.reverse(tasks);
// Load task content // Load task content
loadTaskContents(tasks, () -> { for (Task task : tasks) {
mTaskList = tasks; int loadedPos = mTaskList.indexOf(task);
if (onTasksLoadedCallback != null) { if (loadedPos == -1) {
onTasksLoadedCallback.accept(mTaskList); 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. * Removes the task from the current task list.
*/ */
@ -98,42 +140,4 @@ public final class TaskListLoader {
void clearAllTasks() { void clearAllTasks() {
mTaskList.clear(); 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.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.View; import android.view.View;
@ -24,6 +25,8 @@ import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.Nullable;
import com.android.launcher3.R; import com.android.launcher3.R;
/** /**
@ -31,12 +34,16 @@ import com.android.launcher3.R;
*/ */
public final class TaskItemView extends LinearLayout { public final class TaskItemView extends LinearLayout {
private static final String DEFAULT_LABEL = "...";
private final Drawable mDefaultIcon;
private TextView mLabelView; private TextView mLabelView;
private ImageView mIconView; private ImageView mIconView;
private ImageView mThumbnailView; private ImageView mThumbnailView;
public TaskItemView(Context context, AttributeSet attrs) { public TaskItemView(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);
mDefaultIcon = context.getResources().getDrawable(
android.R.drawable.sym_def_app_icon, context.getTheme());
} }
@Override @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 * @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); 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 * @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 // 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 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 // 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. // 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); 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 * @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); mThumbnailView.setImageBitmap(thumbnail);
} }