Merge "Show ASAP for Recents Go and load content after" into ub-launcher3-master
This commit is contained in:
commit
7c1d69e515
|
@ -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
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue