1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.quickstep; 17 18 import android.view.LayoutInflater; 19 import android.view.View; 20 import android.view.View.OnClickListener; 21 import android.view.ViewGroup; 22 import android.widget.Button; 23 24 import androidx.annotation.NonNull; 25 import androidx.recyclerview.widget.RecyclerView.Adapter; 26 import androidx.recyclerview.widget.RecyclerView.ViewHolder; 27 28 import com.android.launcher3.R; 29 import com.android.quickstep.views.TaskItemView; 30 import com.android.systemui.shared.recents.model.Task; 31 32 import java.util.List; 33 import java.util.Objects; 34 import java.util.Optional; 35 36 /** 37 * Recycler view adapter that dynamically inflates and binds {@link TaskHolder} instances with the 38 * appropriate {@link Task} from the recents task list. 39 */ 40 public final class TaskAdapter extends Adapter<ViewHolder> { 41 42 public static final int CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT = 0; 43 public static final int MAX_TASKS_TO_DISPLAY = 6; 44 public static final int TASKS_START_POSITION = 1; 45 46 public static final int ITEM_TYPE_TASK = 0; 47 public static final int ITEM_TYPE_CLEAR_ALL = 1; 48 49 private static final String TAG = "TaskAdapter"; 50 private final TaskListLoader mLoader; 51 private TaskActionController mTaskActionController; 52 private OnClickListener mClearAllListener; 53 private boolean mIsShowingLoadingUi; 54 TaskAdapter(@onNull TaskListLoader loader)55 public TaskAdapter(@NonNull TaskListLoader loader) { 56 mLoader = loader; 57 } 58 setActionController(TaskActionController taskActionController)59 public void setActionController(TaskActionController taskActionController) { 60 mTaskActionController = taskActionController; 61 } 62 setOnClearAllClickListener(OnClickListener listener)63 public void setOnClearAllClickListener(OnClickListener listener) { 64 mClearAllListener = listener; 65 } 66 67 /** 68 * Sets all positions in the task adapter to loading views, binding new views if necessary. 69 * This changes the task adapter's view of the data, so the appropriate notify events should be 70 * called in addition to this method to reflect the changes. 71 * 72 * @param isShowingLoadingUi true to bind loading task views to all positions, false to return 73 * to the real data 74 */ setIsShowingLoadingUi(boolean isShowingLoadingUi)75 public void setIsShowingLoadingUi(boolean isShowingLoadingUi) { 76 mIsShowingLoadingUi = isShowingLoadingUi; 77 } 78 79 @Override onCreateViewHolder(ViewGroup parent, int viewType)80 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 81 switch (viewType) { 82 case ITEM_TYPE_TASK: 83 TaskItemView itemView = (TaskItemView) LayoutInflater.from(parent.getContext()) 84 .inflate(R.layout.task_item_view, parent, false); 85 TaskHolder taskHolder = new TaskHolder(itemView); 86 itemView.setOnClickListener( 87 view -> mTaskActionController.launchTaskFromView(taskHolder)); 88 return taskHolder; 89 case ITEM_TYPE_CLEAR_ALL: 90 View clearView = LayoutInflater.from(parent.getContext()) 91 .inflate(R.layout.clear_all_button, parent, false); 92 ClearAllHolder clearAllHolder = new ClearAllHolder(clearView); 93 Button clearViewButton = clearView.findViewById(R.id.clear_all_button); 94 clearViewButton.setOnClickListener(mClearAllListener); 95 return clearAllHolder; 96 default: 97 throw new IllegalArgumentException("No known holder for item type: " + viewType); 98 } 99 } 100 101 @Override onBindViewHolder(ViewHolder holder, int position)102 public void onBindViewHolder(ViewHolder holder, int position) { 103 onBindViewHolderInternal(holder, position, false /* willAnimate */); 104 } 105 106 @Override onBindViewHolder(@onNull ViewHolder holder, int position, @NonNull List<Object> payloads)107 public void onBindViewHolder(@NonNull ViewHolder holder, int position, 108 @NonNull List<Object> payloads) { 109 if (payloads.isEmpty()) { 110 super.onBindViewHolder(holder, position, payloads); 111 return; 112 } 113 int changeType = (int) payloads.get(0); 114 if (changeType == CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT) { 115 // Bind in preparation for animation 116 onBindViewHolderInternal(holder, position, true /* willAnimate */); 117 } else { 118 throw new IllegalArgumentException("Payload content is not a valid change event type: " 119 + changeType); 120 } 121 } 122 onBindViewHolderInternal(@onNull ViewHolder holder, int position, boolean willAnimate)123 private void onBindViewHolderInternal(@NonNull ViewHolder holder, int position, 124 boolean willAnimate) { 125 int itemType = getItemViewType(position); 126 switch (itemType) { 127 case ITEM_TYPE_TASK: 128 TaskHolder taskHolder = (TaskHolder) holder; 129 if (mIsShowingLoadingUi) { 130 taskHolder.bindEmptyUi(); 131 return; 132 } 133 List<Task> tasks = mLoader.getCurrentTaskList(); 134 int taskPos = position - TASKS_START_POSITION; 135 if (taskPos >= tasks.size()) { 136 // Task list has updated. 137 return; 138 } 139 Task task = tasks.get(taskPos); 140 taskHolder.bindTask(task, willAnimate /* willAnimate */); 141 mLoader.loadTaskIconAndLabel(task, () -> { 142 // Ensure holder still has the same task. 143 if (Objects.equals(Optional.of(task), taskHolder.getTask())) { 144 taskHolder.getTaskItemView().setIcon(task.icon); 145 taskHolder.getTaskItemView().setLabel(task.titleDescription); 146 } 147 }); 148 mLoader.loadTaskThumbnail(task, () -> { 149 if (Objects.equals(Optional.of(task), taskHolder.getTask())) { 150 taskHolder.getTaskItemView().setThumbnail(task.thumbnail); 151 } 152 }); 153 break; 154 case ITEM_TYPE_CLEAR_ALL: 155 // Nothing to bind. 156 break; 157 default: 158 throw new IllegalArgumentException("No known holder for item type: " + itemType); 159 } 160 } 161 162 @Override getItemViewType(int position)163 public int getItemViewType(int position) { 164 // Bottom is always clear all button. 165 return (position == 0) ? ITEM_TYPE_CLEAR_ALL : ITEM_TYPE_TASK; 166 } 167 168 @Override getItemCount()169 public int getItemCount() { 170 int itemCount = TASKS_START_POSITION; 171 if (mIsShowingLoadingUi) { 172 // Show loading version of all items. 173 itemCount += MAX_TASKS_TO_DISPLAY; 174 } else { 175 itemCount += Math.min(mLoader.getCurrentTaskList().size(), MAX_TASKS_TO_DISPLAY); 176 } 177 return itemCount; 178 } 179 } 180