diff options
Diffstat (limited to 'quickstep/src')
4 files changed, 325 insertions, 3 deletions
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java index 7608645a63..6a74aac2ba 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java @@ -23,6 +23,8 @@ import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_BOTT import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_EXTRA_NAVIGATION_BAR; import android.animation.Animator; +import android.app.ActivityOptions; +import android.content.ComponentName; import android.graphics.PixelFormat; import android.graphics.Point; import android.view.Gravity; @@ -42,8 +44,13 @@ import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.states.StateAnimationConfig; import com.android.launcher3.touch.ItemClickHandler; import com.android.quickstep.AnimatedFloat; +import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.WindowManagerWrapper; +import java.util.ArrayList; +import java.util.List; + /** * Interfaces with Launcher/WindowManager/SystemUI to determine what to show in TaskbarView. */ @@ -60,11 +67,17 @@ public class TaskbarController { private final TaskbarStateHandler mTaskbarStateHandler; private final TaskbarVisibilityController mTaskbarVisibilityController; private final TaskbarHotseatController mHotseatController; + private final TaskbarRecentsController mRecentsController; private final TaskbarDragController mDragController; // Initialized in init(). private WindowManager.LayoutParams mWindowLayoutParams; + // Contains all loaded Tasks, not yet deduped from Hotseat items. + private List<Task> mLatestLoadedRecentTasks; + // Contains all loaded Hotseat items. + private ItemInfo[] mLatestLoadedHotseatItems; + public TaskbarController(BaseQuickstepLauncher launcher, TaskbarContainerView taskbarContainerView) { mLauncher = launcher; @@ -79,6 +92,8 @@ public class TaskbarController { createTaskbarVisibilityControllerCallbacks()); mHotseatController = new TaskbarHotseatController(mLauncher, createTaskbarHotseatControllerCallbacks()); + mRecentsController = new TaskbarRecentsController(mLauncher, + createTaskbarRecentsControllerCallbacks()); mDragController = new TaskbarDragController(mLauncher); } @@ -101,7 +116,16 @@ public class TaskbarController { return new TaskbarViewCallbacks() { @Override public View.OnClickListener getItemOnClickListener() { - return ItemClickHandler.INSTANCE; + return view -> { + Object tag = view.getTag(); + if (tag instanceof Task) { + Task task = (Task) tag; + ActivityManagerWrapper.getInstance().startActivityFromRecents(task.key, + ActivityOptions.makeBasic()); + } else { + ItemClickHandler.INSTANCE.onClick(view); + } + }; } @Override @@ -116,6 +140,23 @@ public class TaskbarController { @Override public void updateHotseatItems(ItemInfo[] hotseatItemInfos) { mTaskbarView.updateHotseatItems(hotseatItemInfos); + mLatestLoadedHotseatItems = hotseatItemInfos; + dedupeAndUpdateRecentItems(); + } + }; + } + + private TaskbarRecentsControllerCallbacks createTaskbarRecentsControllerCallbacks() { + return new TaskbarRecentsControllerCallbacks() { + @Override + public void updateRecentItems(ArrayList<Task> recentTasks) { + mLatestLoadedRecentTasks = recentTasks; + dedupeAndUpdateRecentItems(); + } + + @Override + public void updateRecentTaskAtIndex(int taskIndex, Task task) { + mTaskbarView.updateRecentTaskAtIndex(taskIndex, task); } }; } @@ -124,11 +165,13 @@ public class TaskbarController { * Initializes the Taskbar, including adding it to the screen. */ public void init() { - mTaskbarView.init(mHotseatController.getNumHotseatIcons()); + mTaskbarView.init(mHotseatController.getNumHotseatIcons(), + mRecentsController.getNumRecentIcons()); addToWindowManager(); mTaskbarStateHandler.setTaskbarCallbacks(createTaskbarStateHandlerCallbacks()); mTaskbarVisibilityController.init(); mHotseatController.init(); + mRecentsController.init(); } private TaskbarStateHandlerCallbacks createTaskbarStateHandlerCallbacks() { @@ -149,6 +192,7 @@ public class TaskbarController { mTaskbarStateHandler.setTaskbarCallbacks(null); mTaskbarVisibilityController.cleanup(); mHotseatController.cleanup(); + mRecentsController.cleanup(); } private void removeFromWindowManager() { @@ -246,6 +290,52 @@ public class TaskbarController { return mTaskbarView.isDraggingItem(); } + private void dedupeAndUpdateRecentItems() { + if (mLatestLoadedRecentTasks == null || mLatestLoadedHotseatItems == null) { + return; + } + + final int numRecentIcons = mRecentsController.getNumRecentIcons(); + + // From most recent to least recently opened. + List<Task> dedupedTasksInDescendingOrder = new ArrayList<>(); + for (int i = mLatestLoadedRecentTasks.size() - 1; i >= 0; i--) { + Task task = mLatestLoadedRecentTasks.get(i); + boolean isTaskInHotseat = false; + for (ItemInfo hotseatItem : mLatestLoadedHotseatItems) { + if (hotseatItem == null) { + continue; + } + ComponentName hotseatActivity = hotseatItem.getTargetComponent(); + if (hotseatActivity != null && task.key.sourceComponent.getPackageName() + .equals(hotseatActivity.getPackageName())) { + isTaskInHotseat = true; + break; + } + } + if (!isTaskInHotseat) { + dedupedTasksInDescendingOrder.add(task); + if (dedupedTasksInDescendingOrder.size() == numRecentIcons) { + break; + } + } + } + + // TaskbarView expects an array of all the recent tasks to show, in the order to show them. + // So we create an array of the proper size, then fill it in such that the most recent items + // are at the end. If there aren't enough elements to fill the array, leave them null. + Task[] tasksArray = new Task[numRecentIcons]; + for (int i = 0; i < tasksArray.length; i++) { + Task task = i >= dedupedTasksInDescendingOrder.size() + ? null + : dedupedTasksInDescendingOrder.get(i); + tasksArray[tasksArray.length - 1 - i] = task; + } + + mTaskbarView.updateRecentTasks(tasksArray); + mRecentsController.loadIconsForTasks(tasksArray); + } + /** * @return Whether the given View is in the same window as Taskbar. */ @@ -283,4 +373,12 @@ public class TaskbarController { protected interface TaskbarHotseatControllerCallbacks { void updateHotseatItems(ItemInfo[] hotseatItemInfos); } + + /** + * Contains methods that TaskbarRecentsController can call to interface with TaskbarController. + */ + protected interface TaskbarRecentsControllerCallbacks { + void updateRecentItems(ArrayList<Task> recentTasks); + void updateRecentTaskAtIndex(int taskIndex, Task task); + } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java index 2318ff96b6..baec8998f3 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java @@ -25,6 +25,7 @@ import android.content.pm.LauncherApps; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Point; +import android.os.UserHandle; import android.view.DragEvent; import android.view.View; @@ -33,6 +34,7 @@ import com.android.launcher3.BubbleTextView; import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.ClipDescriptionCompat; import com.android.systemui.shared.system.LauncherAppsCompat; @@ -102,6 +104,15 @@ public class TaskbarDragController { item.getIntent().getComponent(), null, item.user)); } intent.putExtra(Intent.EXTRA_USER, item.user); + } else if (tag instanceof Task) { + Task task = (Task) tag; + clipDescription = new ClipDescription(task.titleDescription, + new String[] { + ClipDescriptionCompat.MIMETYPE_APPLICATION_TASK + }); + intent = new Intent(); + intent.putExtra(ClipDescriptionCompat.EXTRA_TASK_ID, task.key.id); + intent.putExtra(Intent.EXTRA_USER, UserHandle.of(task.key.userId)); } if (clipDescription != null && intent != null) { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentsController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentsController.java new file mode 100644 index 0000000000..9d4e000da0 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentsController.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2021 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.launcher3.taskbar; + +import com.android.launcher3.BaseQuickstepLauncher; +import com.android.quickstep.RecentsModel; +import com.android.quickstep.util.CancellableTask; +import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.system.TaskStackChangeListener; +import com.android.systemui.shared.system.TaskStackChangeListeners; + +import java.util.ArrayList; + +/** + * Works with TaskbarController to update the TaskbarView's Recent items. + */ +public class TaskbarRecentsController { + + private final int mNumRecentIcons = 2; + private final BaseQuickstepLauncher mLauncher; + private final TaskbarController.TaskbarRecentsControllerCallbacks mTaskbarCallbacks; + private final RecentsModel mRecentsModel; + + private final TaskStackChangeListener mTaskStackChangeListener = new TaskStackChangeListener() { + @Override + public void onTaskStackChanged() { + reloadRecentTasksIfNeeded(); + } + }; + + // TODO: add TaskbarVisualsChangedListener as well (for calendar/clock?) + + // Used to keep track of the last requested task list id, so that we do not request to load the + // tasks again if we have already requested it and the task list has not changed + private int mTaskListChangeId = -1; + + // The current background requests to load the task icons + private CancellableTask[] mIconLoadRequests = new CancellableTask[mNumRecentIcons]; + + public TaskbarRecentsController(BaseQuickstepLauncher launcher, + TaskbarController.TaskbarRecentsControllerCallbacks taskbarCallbacks) { + mLauncher = launcher; + mTaskbarCallbacks = taskbarCallbacks; + mRecentsModel = RecentsModel.INSTANCE.get(mLauncher); + } + + protected void init() { + TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackChangeListener); + reloadRecentTasksIfNeeded(); + } + + protected void cleanup() { + TaskStackChangeListeners.getInstance().unregisterTaskStackListener( + mTaskStackChangeListener); + cancelAllPendingIconLoadTasks(); + } + + private void reloadRecentTasksIfNeeded() { + if (!mRecentsModel.isTaskListValid(mTaskListChangeId)) { + mTaskListChangeId = mRecentsModel.getTasks(this::onRecentTasksChanged); + } + } + + private void cancelAllPendingIconLoadTasks() { + for (int i = 0; i < mIconLoadRequests.length; i++) { + if (mIconLoadRequests[i] != null) { + mIconLoadRequests[i].cancel(); + } + mIconLoadRequests[i] = null; + } + } + + private void onRecentTasksChanged(ArrayList<Task> tasks) { + mTaskbarCallbacks.updateRecentItems(tasks); + } + + /** + * For each Task, loads its icon from the cache in the background, then calls + * {@link TaskbarController.TaskbarRecentsControllerCallbacks#updateRecentTaskAtIndex}. + */ + protected void loadIconsForTasks(Task[] tasks) { + cancelAllPendingIconLoadTasks(); + for (int i = 0; i < tasks.length; i++) { + Task task = tasks[i]; + if (task == null) { + continue; + } + final int taskIndex = i; + mIconLoadRequests[i] = mRecentsModel.getIconCache().updateIconInBackground( + task, updatedTask -> onTaskIconLoaded(task, taskIndex)); + } + } + + private void onTaskIconLoaded(Task task, int taskIndex) { + mTaskbarCallbacks.updateRecentTaskAtIndex(taskIndex, task); + } + + protected int getNumRecentIcons() { + return mNumRecentIcons; + } +} diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java index c98f09ca0f..d8f3bb595e 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java @@ -19,6 +19,7 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.RectF; import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.DragEvent; import android.view.LayoutInflater; @@ -35,6 +36,7 @@ import com.android.launcher3.BubbleTextView; import com.android.launcher3.R; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.systemui.shared.recents.model.Task; /** * Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps. @@ -52,6 +54,9 @@ public class TaskbarView extends LinearLayout { // Initialized in init(). private int mHotseatStartIndex; private int mHotseatEndIndex; + private View mHotseatRecentsDivider; + private int mRecentsStartIndex; + private int mRecentsEndIndex; private TaskbarController.TaskbarViewCallbacks mControllerCallbacks; @@ -89,10 +94,17 @@ public class TaskbarView extends LinearLayout { mControllerCallbacks = taskbarViewCallbacks; } - protected void init(int numHotseatIcons) { + protected void init(int numHotseatIcons, int numRecentIcons) { mHotseatStartIndex = 0; mHotseatEndIndex = mHotseatStartIndex + numHotseatIcons - 1; updateHotseatItems(new ItemInfo[numHotseatIcons]); + + int dividerIndex = mHotseatEndIndex + 1; + mHotseatRecentsDivider = addDivider(dividerIndex); + + mRecentsStartIndex = dividerIndex + 1; + mRecentsEndIndex = mRecentsStartIndex + numRecentIcons - 1; + updateRecentTasks(new Task[numRecentIcons]); } protected void cleanup() { @@ -147,6 +159,93 @@ public class TaskbarView extends LinearLayout { hotseatView.setOnLongClickListener(null); } } + + updateHotseatRecentsDividerVisibility(); + } + + private View addDivider(int dividerIndex) { + View divider = inflate(R.layout.taskbar_divider); + addView(divider, dividerIndex); + return divider; + } + + /** + * Inflates/binds the Recents items to show in the Taskbar given their Tasks. + */ + protected void updateRecentTasks(Task[] tasks) { + for (int i = 0; i < tasks.length; i++) { + Task task = tasks[i]; + int recentsIndex = mRecentsStartIndex + i; + View recentsView = getChildAt(recentsIndex); + + // Inflate empty icon Views. + if (recentsView == null) { + BubbleTextView btv = (BubbleTextView) inflate(R.layout.taskbar_app_icon); + LayoutParams lp = new LayoutParams(btv.getIconSize(), btv.getIconSize()); + lp.setMargins(mItemMarginLeftRight, 0, mItemMarginLeftRight, 0); + recentsView = btv; + addView(recentsView, recentsIndex, lp); + } + + // Apply the Task, or hide the view if there is none for a given index. + if (recentsView instanceof BubbleTextView && task != null) { + applyTaskToBubbleTextView((BubbleTextView) recentsView, task); + recentsView.setVisibility(VISIBLE); + recentsView.setOnClickListener(mControllerCallbacks.getItemOnClickListener()); + recentsView.setOnLongClickListener( + mControllerCallbacks.getItemOnLongClickListener()); + } else { + recentsView.setVisibility(GONE); + recentsView.setOnClickListener(null); + recentsView.setOnLongClickListener(null); + } + } + + updateHotseatRecentsDividerVisibility(); + } + + private void applyTaskToBubbleTextView(BubbleTextView btv, Task task) { + if (task.icon != null) { + Drawable icon = task.icon.getConstantState().newDrawable().mutate(); + btv.applyIconAndLabel(icon, task.titleDescription); + } + btv.setTag(task); + } + + protected void updateRecentTaskAtIndex(int taskIndex, Task task) { + View taskView = getChildAt(mRecentsStartIndex + taskIndex); + if (taskView instanceof BubbleTextView) { + applyTaskToBubbleTextView((BubbleTextView) taskView, task); + } + } + + /** + * Make the divider VISIBLE between the Hotseat and Recents if there is at least one icon in + * each, otherwise make it GONE. + */ + private void updateHotseatRecentsDividerVisibility() { + if (mHotseatRecentsDivider == null) { + return; + } + + boolean hasAtLeastOneHotseatItem = false; + for (int i = mHotseatStartIndex; i <= mHotseatEndIndex; i++) { + if (getChildAt(i).getVisibility() != GONE) { + hasAtLeastOneHotseatItem = true; + break; + } + } + + boolean hasAtLeastOneRecentItem = false; + for (int i = mRecentsStartIndex; i <= mRecentsEndIndex; i++) { + if (getChildAt(i).getVisibility() != GONE) { + hasAtLeastOneRecentItem = true; + break; + } + } + + mHotseatRecentsDivider.setVisibility(hasAtLeastOneHotseatItem && hasAtLeastOneRecentItem + ? VISIBLE : GONE); } @Override |