Merge "Desktop tile that is a snapshot of desktop" into tm-qpr-dev am: 24dd3f57b2
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/20027847 Change-Id: I85c9d13c4a1bf329ae68aaf3984aa40859ac89f5 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2022 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<com.android.quickstep.views.DesktopTaskView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:clipChildren="true"
|
||||||
|
android:clipToOutline="true"
|
||||||
|
android:defaultFocusHighlightEnabled="false"
|
||||||
|
android:focusable="true">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
TODO(b249371338): DesktopTaskView extends from TaskView. TaskView expects TaskThumbnailView
|
||||||
|
and IconView with these ids to be present. Need to refactor RecentsView to accept child
|
||||||
|
views that do not inherint from TaskView only or create a generic TaskView that have
|
||||||
|
N number of tasks.
|
||||||
|
-->
|
||||||
|
<com.android.quickstep.views.TaskThumbnailView
|
||||||
|
android:id="@+id/snapshot"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<com.android.quickstep.views.IconView
|
||||||
|
android:id="@+id/icon"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:focusable="false"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
</com.android.quickstep.views.DesktopTaskView>
|
||||||
@@ -1882,6 +1882,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void finishCurrentTransitionToRecents() {
|
private void finishCurrentTransitionToRecents() {
|
||||||
|
// TODO(b/245569277#comment2): enable once isFreeformActive is implemented
|
||||||
mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
|
mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
|
||||||
if (mRecentsAnimationController != null) {
|
if (mRecentsAnimationController != null) {
|
||||||
mRecentsAnimationController.detachNavigationBarFromApp(true);
|
mRecentsAnimationController.detachNavigationBarFromApp(true);
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
package com.android.quickstep;
|
package com.android.quickstep;
|
||||||
|
|
||||||
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
||||||
|
import static com.android.quickstep.views.DesktopTaskView.DESKTOP_MODE_SUPPORTED;
|
||||||
|
import static com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_FREEFORM;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
@@ -30,6 +32,7 @@ import androidx.annotation.VisibleForTesting;
|
|||||||
|
|
||||||
import com.android.launcher3.util.LooperExecutor;
|
import com.android.launcher3.util.LooperExecutor;
|
||||||
import com.android.launcher3.util.SplitConfigurationOptions;
|
import com.android.launcher3.util.SplitConfigurationOptions;
|
||||||
|
import com.android.quickstep.util.DesktopTask;
|
||||||
import com.android.quickstep.util.GroupTask;
|
import com.android.quickstep.util.GroupTask;
|
||||||
import com.android.systemui.shared.recents.model.Task;
|
import com.android.systemui.shared.recents.model.Task;
|
||||||
import com.android.wm.shell.recents.IRecentTasksListener;
|
import com.android.wm.shell.recents.IRecentTasksListener;
|
||||||
@@ -253,8 +256,9 @@ public class RecentTasksList {
|
|||||||
};
|
};
|
||||||
|
|
||||||
TaskLoadResult allTasks = new TaskLoadResult(requestId, loadKeysOnly, rawTasks.size());
|
TaskLoadResult allTasks = new TaskLoadResult(requestId, loadKeysOnly, rawTasks.size());
|
||||||
|
|
||||||
for (GroupedRecentTaskInfo rawTask : rawTasks) {
|
for (GroupedRecentTaskInfo rawTask : rawTasks) {
|
||||||
if (rawTask.getType() == GroupedRecentTaskInfo.TYPE_FREEFORM) {
|
if (DESKTOP_MODE_SUPPORTED && rawTask.getType() == TYPE_FREEFORM) {
|
||||||
GroupTask desktopTask = createDesktopTask(rawTask);
|
GroupTask desktopTask = createDesktopTask(rawTask);
|
||||||
allTasks.add(desktopTask);
|
allTasks.add(desktopTask);
|
||||||
continue;
|
continue;
|
||||||
@@ -284,14 +288,18 @@ public class RecentTasksList {
|
|||||||
return allTasks;
|
return allTasks;
|
||||||
}
|
}
|
||||||
|
|
||||||
private GroupTask createDesktopTask(GroupedRecentTaskInfo taskInfo) {
|
private DesktopTask createDesktopTask(GroupedRecentTaskInfo recentTaskInfo) {
|
||||||
// TODO(b/244348395): create a subclass of GroupTask for desktop tile
|
ArrayList<Task> tasks = new ArrayList<>(recentTaskInfo.getTaskInfoList().size());
|
||||||
// We need a single task information as the primary task. Use the first task
|
for (ActivityManager.RecentTaskInfo taskInfo : recentTaskInfo.getTaskInfoList()) {
|
||||||
Task.TaskKey key = new Task.TaskKey(taskInfo.getTaskInfo1());
|
Task.TaskKey key = new Task.TaskKey(taskInfo);
|
||||||
Task task = new Task(key);
|
Task task = Task.from(key, taskInfo, false);
|
||||||
task.desktopTile = true;
|
task.setLastSnapshotData(taskInfo);
|
||||||
task.topActivity = key.sourceComponent;
|
task.positionInParent = taskInfo.positionInParent;
|
||||||
return new GroupTask(task, null, null);
|
task.appBounds = taskInfo.configuration.windowConfiguration.getAppBounds();
|
||||||
|
// TODO(b/244348395): tasks should be sorted from oldest to most recently used
|
||||||
|
tasks.add(task);
|
||||||
|
}
|
||||||
|
return new DesktopTask(tasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SplitConfigurationOptions.SplitBounds convertSplitBounds(
|
private SplitConfigurationOptions.SplitBounds convertSplitBounds(
|
||||||
@@ -306,7 +314,7 @@ public class RecentTasksList {
|
|||||||
private ArrayList<GroupTask> copyOf(ArrayList<GroupTask> tasks) {
|
private ArrayList<GroupTask> copyOf(ArrayList<GroupTask> tasks) {
|
||||||
ArrayList<GroupTask> newTasks = new ArrayList<>();
|
ArrayList<GroupTask> newTasks = new ArrayList<>();
|
||||||
for (int i = 0; i < tasks.size(); i++) {
|
for (int i = 0; i < tasks.size(); i++) {
|
||||||
newTasks.add(new GroupTask(tasks.get(i)));
|
newTasks.add(tasks.get(i).copy());
|
||||||
}
|
}
|
||||||
return newTasks;
|
return newTasks;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -302,9 +302,15 @@ public final class TaskViewUtils {
|
|||||||
// to follow the TaskViewSimulator. So the final matrix applied on the thumbnailView is:
|
// to follow the TaskViewSimulator. So the final matrix applied on the thumbnailView is:
|
||||||
// Mt K(0)` K(t) Mt`
|
// Mt K(0)` K(t) Mt`
|
||||||
TaskThumbnailView[] thumbnails = v.getThumbnails();
|
TaskThumbnailView[] thumbnails = v.getThumbnails();
|
||||||
Matrix[] mt = new Matrix[simulatorCopies.length];
|
|
||||||
Matrix[] mti = new Matrix[simulatorCopies.length];
|
// In case simulator copies and thumbnail size do no match, ensure we get the lesser.
|
||||||
for (int i = 0; i < thumbnails.length; i++) {
|
// This ensures we do not create arrays with empty elements or attempt to references
|
||||||
|
// indexes out of array bounds.
|
||||||
|
final int matrixSize = Math.min(simulatorCopies.length, thumbnails.length);
|
||||||
|
|
||||||
|
Matrix[] mt = new Matrix[matrixSize];
|
||||||
|
Matrix[] mti = new Matrix[matrixSize];
|
||||||
|
for (int i = 0; i < matrixSize; i++) {
|
||||||
TaskThumbnailView ttv = thumbnails[i];
|
TaskThumbnailView ttv = thumbnails[i];
|
||||||
RectF localBounds = new RectF(0, 0, ttv.getWidth(), ttv.getHeight());
|
RectF localBounds = new RectF(0, 0, ttv.getWidth(), ttv.getHeight());
|
||||||
float[] tvBoundsMapped = new float[]{0, 0, ttv.getWidth(), ttv.getHeight()};
|
float[] tvBoundsMapped = new float[]{0, 0, ttv.getWidth(), ttv.getHeight()};
|
||||||
@@ -321,14 +327,14 @@ public final class TaskViewUtils {
|
|||||||
mti[i] = localMti;
|
mti[i] = localMti;
|
||||||
}
|
}
|
||||||
|
|
||||||
Matrix[] k0i = new Matrix[simulatorCopies.length];
|
Matrix[] k0i = new Matrix[matrixSize];
|
||||||
for (int i = 0; i < simulatorCopies.length; i++) {
|
for (int i = 0; i < matrixSize; i++) {
|
||||||
k0i[i] = new Matrix();
|
k0i[i] = new Matrix();
|
||||||
simulatorCopies[i].getTaskViewSimulator().getCurrentMatrix().invert(k0i[i]);
|
simulatorCopies[i].getTaskViewSimulator().getCurrentMatrix().invert(k0i[i]);
|
||||||
}
|
}
|
||||||
Matrix animationMatrix = new Matrix();
|
Matrix animationMatrix = new Matrix();
|
||||||
out.addOnFrameCallback(() -> {
|
out.addOnFrameCallback(() -> {
|
||||||
for (int i = 0; i < simulatorCopies.length; i++) {
|
for (int i = 0; i < matrixSize; i++) {
|
||||||
animationMatrix.set(mt[i]);
|
animationMatrix.set(mt[i]);
|
||||||
animationMatrix.postConcat(k0i[i]);
|
animationMatrix.postConcat(k0i[i]);
|
||||||
animationMatrix.postConcat(simulatorCopies[i]
|
animationMatrix.postConcat(simulatorCopies[i]
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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.quickstep.util;
|
||||||
|
|
||||||
|
import com.android.quickstep.views.TaskView;
|
||||||
|
import com.android.systemui.shared.recents.model.Task;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link Task} container that can contain N number of tasks that are part of the desktop in
|
||||||
|
* recent tasks list.
|
||||||
|
*/
|
||||||
|
public class DesktopTask extends GroupTask {
|
||||||
|
|
||||||
|
public ArrayList<Task> tasks;
|
||||||
|
|
||||||
|
public DesktopTask(ArrayList<Task> tasks) {
|
||||||
|
super(tasks.get(0), null, null, TaskView.Type.DESKTOP);
|
||||||
|
this.tasks = tasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsTask(int taskId) {
|
||||||
|
for (Task task : tasks) {
|
||||||
|
if (task.key.id == taskId) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasMultipleTasks() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DesktopTask copy() {
|
||||||
|
return new DesktopTask(tasks);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
|
import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
|
||||||
|
import com.android.quickstep.views.TaskView;
|
||||||
import com.android.systemui.shared.recents.model.Task;
|
import com.android.systemui.shared.recents.model.Task;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -27,24 +28,25 @@ import com.android.systemui.shared.recents.model.Task;
|
|||||||
* are represented as an app-pair in the recents task list.
|
* are represented as an app-pair in the recents task list.
|
||||||
*/
|
*/
|
||||||
public class GroupTask {
|
public class GroupTask {
|
||||||
public @NonNull Task task1;
|
@NonNull
|
||||||
public @Nullable Task task2;
|
public final Task task1;
|
||||||
public @Nullable
|
@Nullable
|
||||||
SplitBounds mSplitBounds;
|
public final Task task2;
|
||||||
|
@Nullable
|
||||||
|
public final SplitBounds mSplitBounds;
|
||||||
|
@TaskView.Type
|
||||||
|
public final int taskViewType;
|
||||||
|
|
||||||
public GroupTask(@NonNull Task t1, @Nullable Task t2,
|
public GroupTask(@NonNull Task t1, @Nullable Task t2, @Nullable SplitBounds splitBounds) {
|
||||||
@Nullable SplitBounds splitBounds) {
|
this(t1, t2, splitBounds, t2 != null ? TaskView.Type.GROUPED : TaskView.Type.SINGLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected GroupTask(@NonNull Task t1, @Nullable Task t2, @Nullable SplitBounds splitBounds,
|
||||||
|
@TaskView.Type int taskViewType) {
|
||||||
task1 = t1;
|
task1 = t1;
|
||||||
task2 = t2;
|
task2 = t2;
|
||||||
mSplitBounds = splitBounds;
|
mSplitBounds = splitBounds;
|
||||||
}
|
this.taskViewType = taskViewType;
|
||||||
|
|
||||||
public GroupTask(@NonNull GroupTask group) {
|
|
||||||
task1 = new Task(group.task1);
|
|
||||||
task2 = group.task2 != null
|
|
||||||
? new Task(group.task2)
|
|
||||||
: null;
|
|
||||||
mSplitBounds = group.mSplitBounds;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean containsTask(int taskId) {
|
public boolean containsTask(int taskId) {
|
||||||
@@ -54,4 +56,14 @@ public class GroupTask {
|
|||||||
public boolean hasMultipleTasks() {
|
public boolean hasMultipleTasks() {
|
||||||
return task2 != null;
|
return task2 != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a copy of this instance
|
||||||
|
*/
|
||||||
|
public GroupTask copy() {
|
||||||
|
return new GroupTask(
|
||||||
|
new Task(task1),
|
||||||
|
task2 != null ? new Task(task2) : null,
|
||||||
|
mSplitBounds);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,467 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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.quickstep.views;
|
||||||
|
|
||||||
|
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||||
|
|
||||||
|
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Point;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.drawable.ShapeDrawable;
|
||||||
|
import android.graphics.drawable.shapes.RoundRectShape;
|
||||||
|
import android.os.SystemProperties;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.util.SparseArray;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.android.launcher3.DeviceProfile;
|
||||||
|
import com.android.launcher3.Utilities;
|
||||||
|
import com.android.launcher3.touch.PagedOrientationHandler;
|
||||||
|
import com.android.launcher3.util.RunnableList;
|
||||||
|
import com.android.quickstep.RecentsModel;
|
||||||
|
import com.android.quickstep.SystemUiProxy;
|
||||||
|
import com.android.quickstep.TaskThumbnailCache;
|
||||||
|
import com.android.quickstep.util.CancellableTask;
|
||||||
|
import com.android.quickstep.util.RecentsOrientedState;
|
||||||
|
import com.android.systemui.shared.recents.model.Task;
|
||||||
|
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TaskView that contains all tasks that are part of the desktop.
|
||||||
|
*/
|
||||||
|
// TODO(b/249371338): TaskView needs to be refactored to have better support for N tasks.
|
||||||
|
public class DesktopTaskView extends TaskView {
|
||||||
|
|
||||||
|
/** Flag to indicate whether desktop mode is available on the device */
|
||||||
|
public static final boolean DESKTOP_MODE_SUPPORTED = SystemProperties.getBoolean(
|
||||||
|
"persist.wm.debug.desktop_mode", false);
|
||||||
|
|
||||||
|
private static final String TAG = DesktopTaskView.class.getSimpleName();
|
||||||
|
|
||||||
|
private static final boolean DEBUG = true;
|
||||||
|
|
||||||
|
private List<Task> mTasks;
|
||||||
|
|
||||||
|
private final ArrayList<TaskThumbnailView> mSnapshotViews = new ArrayList<>();
|
||||||
|
|
||||||
|
/** Maps {@code taskIds} to corresponding {@link TaskThumbnailView}s */
|
||||||
|
private final SparseArray<TaskThumbnailView> mSnapshotViewMap = new SparseArray<>();
|
||||||
|
|
||||||
|
private final ArrayList<CancellableTask<?>> mPendingThumbnailRequests = new ArrayList<>();
|
||||||
|
|
||||||
|
public DesktopTaskView(Context context) {
|
||||||
|
this(context, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DesktopTaskView(Context context, AttributeSet attrs) {
|
||||||
|
this(context, attrs, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DesktopTaskView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onFinishInflate() {
|
||||||
|
super.onFinishInflate();
|
||||||
|
float[] outerRadii = new float[8];
|
||||||
|
Arrays.fill(outerRadii, getTaskCornerRadius());
|
||||||
|
RoundRectShape shape = new RoundRectShape(outerRadii, null, null);
|
||||||
|
ShapeDrawable background = new ShapeDrawable(shape);
|
||||||
|
background.setTint(getResources().getColor(android.R.color.system_neutral2_300));
|
||||||
|
// TODO(b/244348395): this should be wallpaper
|
||||||
|
setBackground(background);
|
||||||
|
|
||||||
|
mSnapshotViews.add(mSnapshotView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bind(Task task, RecentsOrientedState orientedState) {
|
||||||
|
bind(Collections.singletonList(task), orientedState);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates this desktop task to the gives task list defined in {@code tasks}
|
||||||
|
*/
|
||||||
|
public void bind(List<Task> tasks, RecentsOrientedState orientedState) {
|
||||||
|
if (DEBUG) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("bind tasks=").append(tasks.size()).append("\n");
|
||||||
|
for (Task task : tasks) {
|
||||||
|
sb.append(" key=").append(task.key).append("\n");
|
||||||
|
}
|
||||||
|
Log.d(TAG, sb.toString());
|
||||||
|
}
|
||||||
|
if (tasks.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cancelPendingLoadTasks();
|
||||||
|
|
||||||
|
mTasks = tasks;
|
||||||
|
mSnapshotViewMap.clear();
|
||||||
|
|
||||||
|
// Ensure there are equal number of snapshot views and tasks.
|
||||||
|
// More tasks than views, add views. More views than tasks, remove views.
|
||||||
|
// TODO(b/251586230): use a ViewPool for creating TaskThumbnailViews
|
||||||
|
if (mSnapshotViews.size() > mTasks.size()) {
|
||||||
|
int diff = mSnapshotViews.size() - mTasks.size();
|
||||||
|
for (int i = 0; i < diff; i++) {
|
||||||
|
TaskThumbnailView snapshotView = mSnapshotViews.remove(0);
|
||||||
|
removeView(snapshotView);
|
||||||
|
}
|
||||||
|
} else if (mSnapshotViews.size() < mTasks.size()) {
|
||||||
|
int diff = mTasks.size() - mSnapshotViews.size();
|
||||||
|
for (int i = 0; i < diff; i++) {
|
||||||
|
TaskThumbnailView snapshotView = new TaskThumbnailView(getContext());
|
||||||
|
mSnapshotViews.add(snapshotView);
|
||||||
|
addView(snapshotView, new LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < mTasks.size(); i++) {
|
||||||
|
Task task = mTasks.get(i);
|
||||||
|
TaskThumbnailView snapshotView = mSnapshotViews.get(i);
|
||||||
|
snapshotView.bind(task);
|
||||||
|
mSnapshotViewMap.put(task.key.id, snapshotView);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTaskIdContainer();
|
||||||
|
updateTaskIdAttributeContainer();
|
||||||
|
|
||||||
|
setOrientationState(orientedState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTaskIdContainer() {
|
||||||
|
// TODO(b/249371338): TaskView expects the array to have at least 2 elements.
|
||||||
|
// At least 2 elements in the array
|
||||||
|
mTaskIdContainer = new int[Math.max(mTasks.size(), 2)];
|
||||||
|
for (int i = 0; i < mTasks.size(); i++) {
|
||||||
|
mTaskIdContainer[i] = mTasks.get(i).key.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTaskIdAttributeContainer() {
|
||||||
|
// TODO(b/249371338): TaskView expects the array to have at least 2 elements.
|
||||||
|
// At least 2 elements in the array
|
||||||
|
mTaskIdAttributeContainer = new TaskIdAttributeContainer[Math.max(mTasks.size(), 2)];
|
||||||
|
for (int i = 0; i < mTasks.size(); i++) {
|
||||||
|
Task task = mTasks.get(i);
|
||||||
|
TaskThumbnailView thumbnailView = mSnapshotViewMap.get(task.key.id);
|
||||||
|
mTaskIdAttributeContainer[i] = createAttributeContainer(task, thumbnailView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TaskIdAttributeContainer createAttributeContainer(Task task,
|
||||||
|
TaskThumbnailView thumbnailView) {
|
||||||
|
return new TaskIdAttributeContainer(task, thumbnailView, null, STAGE_POSITION_UNDEFINED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Task getTask() {
|
||||||
|
// TODO(b/249371338): returning first task. This won't work well with multiple tasks.
|
||||||
|
return mTasks.size() > 0 ? mTasks.get(0) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TaskThumbnailView getThumbnail() {
|
||||||
|
// TODO(b/249371338): returning single thumbnail. This won't work well with multiple tasks.
|
||||||
|
Task task = getTask();
|
||||||
|
if (task != null) {
|
||||||
|
return mSnapshotViewMap.get(task.key.id);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsTaskId(int taskId) {
|
||||||
|
// Thumbnail map contains taskId -> thumbnail map. Use the keys for contains
|
||||||
|
return mSnapshotViewMap.contains(taskId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTaskListVisibilityChanged(boolean visible, int changes) {
|
||||||
|
cancelPendingLoadTasks();
|
||||||
|
if (visible) {
|
||||||
|
RecentsModel model = RecentsModel.INSTANCE.get(getContext());
|
||||||
|
TaskThumbnailCache thumbnailCache = model.getThumbnailCache();
|
||||||
|
|
||||||
|
if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
|
||||||
|
for (Task task : mTasks) {
|
||||||
|
CancellableTask<?> thumbLoadRequest =
|
||||||
|
thumbnailCache.updateThumbnailInBackground(task, thumbnailData -> {
|
||||||
|
TaskThumbnailView thumbnailView = mSnapshotViewMap.get(task.key.id);
|
||||||
|
if (thumbnailView != null) {
|
||||||
|
thumbnailView.setThumbnail(task, thumbnailData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (thumbLoadRequest != null) {
|
||||||
|
mPendingThumbnailRequests.add(thumbLoadRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
|
||||||
|
for (Task task : mTasks) {
|
||||||
|
TaskThumbnailView thumbnailView = mSnapshotViewMap.get(task.key.id);
|
||||||
|
if (thumbnailView != null) {
|
||||||
|
thumbnailView.setThumbnail(null, null);
|
||||||
|
}
|
||||||
|
// Reset the task thumbnail ref
|
||||||
|
task.thumbnail = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOrientationState(RecentsOrientedState orientationState) {
|
||||||
|
// TODO(b/249371338): this copies logic from TaskView
|
||||||
|
PagedOrientationHandler orientationHandler = orientationState.getOrientationHandler();
|
||||||
|
boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
|
||||||
|
DeviceProfile deviceProfile = mActivity.getDeviceProfile();
|
||||||
|
|
||||||
|
LayoutParams iconParams = (LayoutParams) mIconView.getLayoutParams();
|
||||||
|
|
||||||
|
int thumbnailTopMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
|
||||||
|
int taskIconHeight = deviceProfile.overviewTaskIconSizePx;
|
||||||
|
int taskMargin = deviceProfile.overviewTaskMarginPx;
|
||||||
|
|
||||||
|
orientationHandler.setTaskIconParams(iconParams, taskMargin, taskIconHeight,
|
||||||
|
thumbnailTopMargin, isRtl);
|
||||||
|
|
||||||
|
LayoutParams snapshotParams = (LayoutParams) mSnapshotView.getLayoutParams();
|
||||||
|
snapshotParams.topMargin = thumbnailTopMargin;
|
||||||
|
|
||||||
|
for (int i = 0; i < mSnapshotViewMap.size(); i++) {
|
||||||
|
TaskThumbnailView thumbnailView = mSnapshotViewMap.valueAt(i);
|
||||||
|
thumbnailView.setLayoutParams(snapshotParams);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void cancelPendingLoadTasks() {
|
||||||
|
for (CancellableTask<?> cancellableTask : mPendingThumbnailRequests) {
|
||||||
|
cancellableTask.cancel();
|
||||||
|
}
|
||||||
|
mPendingThumbnailRequests.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean offerTouchToChildren(MotionEvent event) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean showTaskMenuWithContainer(IconView iconView) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public RunnableList launchTaskAnimated() {
|
||||||
|
RunnableList endCallback = new RunnableList();
|
||||||
|
SystemUiProxy.INSTANCE.get(getContext()).showDesktopApps();
|
||||||
|
RecentsView<?, ?> recentsView = getRecentsView();
|
||||||
|
recentsView.addSideTaskLaunchCallback(endCallback);
|
||||||
|
return endCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void launchTask(@NonNull Consumer<Boolean> callback, boolean freezeTaskList) {
|
||||||
|
SystemUiProxy.INSTANCE.get(getContext()).showDesktopApps();
|
||||||
|
callback.accept(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void refreshThumbnails(@Nullable HashMap<Integer, ThumbnailData> thumbnailDatas) {
|
||||||
|
// Sets new thumbnails based on the incoming data and refreshes the rest.
|
||||||
|
// Create a copy of the thumbnail map, so we can track thumbnails that need refreshing.
|
||||||
|
SparseArray<TaskThumbnailView> thumbnailsToRefresh = mSnapshotViewMap.clone();
|
||||||
|
if (thumbnailDatas != null) {
|
||||||
|
for (Task task : mTasks) {
|
||||||
|
int key = task.key.id;
|
||||||
|
TaskThumbnailView thumbnailView = thumbnailsToRefresh.get(key);
|
||||||
|
ThumbnailData thumbnailData = thumbnailDatas.get(key);
|
||||||
|
if (thumbnailView != null && thumbnailData != null) {
|
||||||
|
thumbnailView.setThumbnail(task, thumbnailData);
|
||||||
|
// Remove this thumbnail from the list that should be refreshed.
|
||||||
|
thumbnailsToRefresh.remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh the rest that were not updated.
|
||||||
|
for (int i = 0; i < thumbnailsToRefresh.size(); i++) {
|
||||||
|
thumbnailsToRefresh.valueAt(i).refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TaskThumbnailView[] getThumbnails() {
|
||||||
|
TaskThumbnailView[] thumbnails = new TaskThumbnailView[mSnapshotViewMap.size()];
|
||||||
|
for (int i = 0; i < thumbnails.length; i++) {
|
||||||
|
thumbnails[i] = mSnapshotViewMap.valueAt(i);
|
||||||
|
}
|
||||||
|
return thumbnails;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRecycle() {
|
||||||
|
resetPersistentViewTransforms();
|
||||||
|
// Clear any references to the thumbnail (it will be re-read either from the cache or the
|
||||||
|
// system on next bind)
|
||||||
|
for (Task task : mTasks) {
|
||||||
|
TaskThumbnailView thumbnailView = mSnapshotViewMap.get(task.key.id);
|
||||||
|
if (thumbnailView != null) {
|
||||||
|
thumbnailView.setThumbnail(task, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setOverlayEnabled(false);
|
||||||
|
onTaskListVisibilityChanged(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||||
|
int containerWidth = MeasureSpec.getSize(widthMeasureSpec);
|
||||||
|
int containerHeight = MeasureSpec.getSize(heightMeasureSpec);
|
||||||
|
|
||||||
|
setMeasuredDimension(containerWidth, containerHeight);
|
||||||
|
|
||||||
|
int thumbnails = mSnapshotViewMap.size();
|
||||||
|
if (thumbnails == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int windowWidth = mActivity.getDeviceProfile().widthPx;
|
||||||
|
int windowHeight = mActivity.getDeviceProfile().heightPx;
|
||||||
|
|
||||||
|
float scaleWidth = containerWidth / (float) windowWidth;
|
||||||
|
float scaleHeight = containerHeight / (float) windowHeight;
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG,
|
||||||
|
"onMeasure: container=[" + containerWidth + "," + containerHeight + "] window=["
|
||||||
|
+ windowWidth + "," + windowHeight + "] scale=[" + scaleWidth + ","
|
||||||
|
+ scaleHeight + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Desktop tile is a shrunk down version of launcher and freeform task thumbnails.
|
||||||
|
for (int i = 0; i < mTasks.size(); i++) {
|
||||||
|
Task task = mTasks.get(i);
|
||||||
|
Rect taskSize = task.appBounds;
|
||||||
|
if (taskSize == null) {
|
||||||
|
// Default to quarter of the desktop if we did not get app bounds.
|
||||||
|
taskSize = new Rect(0, 0, windowWidth / 4, windowHeight / 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
int thumbWidth = (int) (taskSize.width() * scaleWidth);
|
||||||
|
int thumbHeight = (int) (taskSize.height() * scaleHeight);
|
||||||
|
|
||||||
|
TaskThumbnailView thumbnailView = mSnapshotViewMap.get(task.key.id);
|
||||||
|
if (thumbnailView != null) {
|
||||||
|
thumbnailView.measure(MeasureSpec.makeMeasureSpec(thumbWidth, MeasureSpec.EXACTLY),
|
||||||
|
MeasureSpec.makeMeasureSpec(thumbHeight, MeasureSpec.EXACTLY));
|
||||||
|
|
||||||
|
// Position the task to the same position as it would be on the desktop
|
||||||
|
Point positionInParent = task.positionInParent;
|
||||||
|
if (positionInParent == null) {
|
||||||
|
positionInParent = new Point(0, 0);
|
||||||
|
}
|
||||||
|
int taskX = (int) (positionInParent.x * scaleWidth);
|
||||||
|
int taskY = (int) (positionInParent.y * scaleHeight);
|
||||||
|
thumbnailView.setX(taskX);
|
||||||
|
thumbnailView.setY(taskY);
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "onMeasure: task=" + task.key + " thumb=[" + thumbWidth + ","
|
||||||
|
+ thumbHeight + "]" + " pos=[" + taskX + "," + taskY + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOverlayEnabled(boolean overlayEnabled) {
|
||||||
|
// Intentional no-op to prevent setting smart actions overlay on thumbnails
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFullscreenProgress(float progress) {
|
||||||
|
// TODO(b/249371338): this copies parent implementation and makes it work for N thumbs
|
||||||
|
progress = Utilities.boundToRange(progress, 0, 1);
|
||||||
|
mFullscreenProgress = progress;
|
||||||
|
for (int i = 0; i < mSnapshotViewMap.size(); i++) {
|
||||||
|
TaskThumbnailView thumbnailView = mSnapshotViewMap.valueAt(i);
|
||||||
|
thumbnailView.getTaskOverlay().setFullscreenProgress(progress);
|
||||||
|
updateSnapshotRadius();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateSnapshotRadius() {
|
||||||
|
for (int i = 0; i < mSnapshotViewMap.size(); i++) {
|
||||||
|
mSnapshotViewMap.valueAt(i).setFullscreenParams(mCurrentFullscreenParams);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setIconAndDimTransitionProgress(float progress, boolean invert) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setColorTint(float amount, int tintColor) {
|
||||||
|
for (int i = 0; i < mSnapshotViewMap.size(); i++) {
|
||||||
|
mSnapshotViewMap.valueAt(i).setDimAlpha(amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void applyThumbnailSplashAlpha() {
|
||||||
|
for (int i = 0; i < mSnapshotViewMap.size(); i++) {
|
||||||
|
mSnapshotViewMap.valueAt(i).setSplashAlpha(mTaskThumbnailSplashAlpha);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void setThumbnailVisibility(int visibility) {
|
||||||
|
for (int i = 0; i < mSnapshotViewMap.size(); i++) {
|
||||||
|
mSnapshotViewMap.valueAt(i).setVisibility(visibility);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean confirmSecondSplitSelectApp() {
|
||||||
|
// Desktop tile can't be in split screen
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -224,6 +224,12 @@ public class GroupedTaskView extends TaskView {
|
|||||||
mSnapshotView2.refresh();
|
mSnapshotView2.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsTaskId(int taskId) {
|
||||||
|
return (mTask != null && mTask.key.id == taskId)
|
||||||
|
|| (mSecondaryTask != null && mSecondaryTask.key.id == taskId);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TaskThumbnailView[] getThumbnails() {
|
public TaskThumbnailView[] getThumbnails() {
|
||||||
return new TaskThumbnailView[]{mSnapshotView, mSnapshotView2};
|
return new TaskThumbnailView[]{mSnapshotView, mSnapshotView2};
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITIO
|
|||||||
import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
|
import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
|
||||||
import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
|
import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
|
||||||
import static com.android.quickstep.views.ClearAllButton.DISMISS_ALPHA;
|
import static com.android.quickstep.views.ClearAllButton.DISMISS_ALPHA;
|
||||||
|
import static com.android.quickstep.views.DesktopTaskView.DESKTOP_MODE_SUPPORTED;
|
||||||
import static com.android.quickstep.views.OverviewActionsView.FLAG_IS_NOT_TABLET;
|
import static com.android.quickstep.views.OverviewActionsView.FLAG_IS_NOT_TABLET;
|
||||||
import static com.android.quickstep.views.OverviewActionsView.FLAG_SINGLE_TASK;
|
import static com.android.quickstep.views.OverviewActionsView.FLAG_SINGLE_TASK;
|
||||||
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NON_ZERO_ROTATION;
|
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NON_ZERO_ROTATION;
|
||||||
@@ -72,6 +73,7 @@ import android.animation.PropertyValuesHolder;
|
|||||||
import android.animation.ValueAnimator;
|
import android.animation.ValueAnimator;
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
|
import android.app.WindowConfiguration;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.LocusId;
|
import android.content.LocusId;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
@@ -171,6 +173,7 @@ import com.android.quickstep.ViewUtils;
|
|||||||
import com.android.quickstep.util.ActiveGestureErrorDetector;
|
import com.android.quickstep.util.ActiveGestureErrorDetector;
|
||||||
import com.android.quickstep.util.ActiveGestureLog;
|
import com.android.quickstep.util.ActiveGestureLog;
|
||||||
import com.android.quickstep.util.AnimUtils;
|
import com.android.quickstep.util.AnimUtils;
|
||||||
|
import com.android.quickstep.util.DesktopTask;
|
||||||
import com.android.quickstep.util.GroupTask;
|
import com.android.quickstep.util.GroupTask;
|
||||||
import com.android.quickstep.util.LayoutUtils;
|
import com.android.quickstep.util.LayoutUtils;
|
||||||
import com.android.quickstep.util.RecentsOrientedState;
|
import com.android.quickstep.util.RecentsOrientedState;
|
||||||
@@ -195,6 +198,7 @@ import com.android.systemui.shared.system.TaskStackChangeListeners;
|
|||||||
import com.android.wm.shell.pip.IPipAnimationListener;
|
import com.android.wm.shell.pip.IPipAnimationListener;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@@ -477,10 +481,11 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
|||||||
private final InvariantDeviceProfile mIdp;
|
private final InvariantDeviceProfile mIdp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Getting views should be done via {@link #getTaskViewFromPool(boolean)}
|
* Getting views should be done via {@link #getTaskViewFromPool(int)}
|
||||||
*/
|
*/
|
||||||
private final ViewPool<TaskView> mTaskViewPool;
|
private final ViewPool<TaskView> mTaskViewPool;
|
||||||
private final ViewPool<GroupedTaskView> mGroupedTaskViewPool;
|
private final ViewPool<GroupedTaskView> mGroupedTaskViewPool;
|
||||||
|
private final ViewPool<DesktopTaskView> mDesktopTaskViewPool;
|
||||||
|
|
||||||
private final TaskOverlayFactory mTaskOverlayFactory;
|
private final TaskOverlayFactory mTaskOverlayFactory;
|
||||||
|
|
||||||
@@ -737,6 +742,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
|||||||
10 /* initial size */);
|
10 /* initial size */);
|
||||||
mGroupedTaskViewPool = new ViewPool<>(context, this,
|
mGroupedTaskViewPool = new ViewPool<>(context, this,
|
||||||
R.layout.task_grouped, 20 /* max size */, 10 /* initial size */);
|
R.layout.task_grouped, 20 /* max size */, 10 /* initial size */);
|
||||||
|
mDesktopTaskViewPool = new ViewPool<>(context, this, R.layout.task_desktop,
|
||||||
|
5 /* max size */, 1 /* initial size */);
|
||||||
|
|
||||||
mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources());
|
mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources());
|
||||||
setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
|
setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
|
||||||
@@ -981,6 +988,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
|||||||
}
|
}
|
||||||
if (child instanceof GroupedTaskView) {
|
if (child instanceof GroupedTaskView) {
|
||||||
mGroupedTaskViewPool.recycle((GroupedTaskView) taskView);
|
mGroupedTaskViewPool.recycle((GroupedTaskView) taskView);
|
||||||
|
} else if (child instanceof DesktopTaskView) {
|
||||||
|
mDesktopTaskViewPool.recycle((DesktopTaskView) taskView);
|
||||||
} else {
|
} else {
|
||||||
mTaskViewPool.recycle(taskView);
|
mTaskViewPool.recycle(taskView);
|
||||||
}
|
}
|
||||||
@@ -1199,8 +1208,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
|||||||
|
|
||||||
for (int i = 0; i < getTaskViewCount(); i++) {
|
for (int i = 0; i < getTaskViewCount(); i++) {
|
||||||
TaskView taskView = requireTaskViewAt(i);
|
TaskView taskView = requireTaskViewAt(i);
|
||||||
int[] taskIds = taskView.getTaskIds();
|
if (taskView.containsTaskId(taskId)) {
|
||||||
if (taskIds[0] == taskId || taskIds[1] == taskId) {
|
|
||||||
return taskView;
|
return taskView;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1481,21 +1489,24 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
|||||||
// Add views as children based on whether it's grouped or single task
|
// Add views as children based on whether it's grouped or single task
|
||||||
for (int i = taskGroups.size() - 1; i >= 0; i--) {
|
for (int i = taskGroups.size() - 1; i >= 0; i--) {
|
||||||
GroupTask groupTask = taskGroups.get(i);
|
GroupTask groupTask = taskGroups.get(i);
|
||||||
boolean hasMultipleTasks = groupTask.hasMultipleTasks();
|
TaskView taskView = getTaskViewFromPool(groupTask.taskViewType);
|
||||||
TaskView taskView = getTaskViewFromPool(hasMultipleTasks);
|
|
||||||
addView(taskView);
|
addView(taskView);
|
||||||
|
|
||||||
if (hasMultipleTasks) {
|
if (taskView instanceof GroupedTaskView) {
|
||||||
boolean firstTaskIsLeftTopTask =
|
boolean firstTaskIsLeftTopTask =
|
||||||
groupTask.mSplitBounds.leftTopTaskId == groupTask.task1.key.id;
|
groupTask.mSplitBounds.leftTopTaskId == groupTask.task1.key.id;
|
||||||
Task leftTopTask = firstTaskIsLeftTopTask ? groupTask.task1 : groupTask.task2;
|
Task leftTopTask = firstTaskIsLeftTopTask ? groupTask.task1 : groupTask.task2;
|
||||||
Task rightBottomTask = firstTaskIsLeftTopTask ? groupTask.task2 : groupTask.task1;
|
Task rightBottomTask = firstTaskIsLeftTopTask ? groupTask.task2 : groupTask.task1;
|
||||||
((GroupedTaskView) taskView).bind(leftTopTask, rightBottomTask, mOrientationState,
|
((GroupedTaskView) taskView).bind(leftTopTask, rightBottomTask, mOrientationState,
|
||||||
groupTask.mSplitBounds);
|
groupTask.mSplitBounds);
|
||||||
|
} else if (taskView instanceof DesktopTaskView) {
|
||||||
|
((DesktopTaskView) taskView).bind(((DesktopTask) groupTask).tasks,
|
||||||
|
mOrientationState);
|
||||||
} else {
|
} else {
|
||||||
taskView.bind(groupTask.task1, mOrientationState);
|
taskView.bind(groupTask.task1, mOrientationState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!taskGroups.isEmpty()) {
|
if (!taskGroups.isEmpty()) {
|
||||||
addView(mClearAllButton);
|
addView(mClearAllButton);
|
||||||
}
|
}
|
||||||
@@ -2123,10 +2134,19 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
|||||||
* Handle the edge case where Recents could increment task count very high over long
|
* Handle the edge case where Recents could increment task count very high over long
|
||||||
* period of device usage. Probably will never happen, but meh.
|
* period of device usage. Probably will never happen, but meh.
|
||||||
*/
|
*/
|
||||||
private <T extends TaskView> T getTaskViewFromPool(boolean isGrouped) {
|
private TaskView getTaskViewFromPool(@TaskView.Type int type) {
|
||||||
T taskView = isGrouped ?
|
TaskView taskView;
|
||||||
(T) mGroupedTaskViewPool.getView() :
|
switch (type) {
|
||||||
(T) mTaskViewPool.getView();
|
case TaskView.Type.GROUPED:
|
||||||
|
taskView = mGroupedTaskViewPool.getView();
|
||||||
|
break;
|
||||||
|
case TaskView.Type.DESKTOP:
|
||||||
|
taskView = mDesktopTaskViewPool.getView();
|
||||||
|
break;
|
||||||
|
case TaskView.Type.SINGLE:
|
||||||
|
default:
|
||||||
|
taskView = mTaskViewPool.getView();
|
||||||
|
}
|
||||||
taskView.setTaskViewId(mTaskViewIdCount);
|
taskView.setTaskViewId(mTaskViewIdCount);
|
||||||
if (mTaskViewIdCount == Integer.MAX_VALUE) {
|
if (mTaskViewIdCount == Integer.MAX_VALUE) {
|
||||||
mTaskViewIdCount = 0;
|
mTaskViewIdCount = 0;
|
||||||
@@ -2318,12 +2338,19 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
|||||||
}
|
}
|
||||||
int runningTaskViewId = -1;
|
int runningTaskViewId = -1;
|
||||||
boolean needGroupTaskView = runningTasks.length > 1;
|
boolean needGroupTaskView = runningTasks.length > 1;
|
||||||
|
boolean needDesktopTask = hasDesktopTask(runningTasks);
|
||||||
if (shouldAddStubTaskView(runningTasks)) {
|
if (shouldAddStubTaskView(runningTasks)) {
|
||||||
boolean wasEmpty = getChildCount() == 0;
|
boolean wasEmpty = getChildCount() == 0;
|
||||||
// Add an empty view for now until the task plan is loaded and applied
|
// Add an empty view for now until the task plan is loaded and applied
|
||||||
final TaskView taskView;
|
final TaskView taskView;
|
||||||
if (needGroupTaskView) {
|
if (needDesktopTask) {
|
||||||
taskView = getTaskViewFromPool(true);
|
taskView = getTaskViewFromPool(TaskView.Type.DESKTOP);
|
||||||
|
mTmpRunningTasks = Arrays.copyOf(runningTasks, runningTasks.length);
|
||||||
|
addView(taskView, 0);
|
||||||
|
((DesktopTaskView) taskView).bind(Arrays.asList(mTmpRunningTasks),
|
||||||
|
mOrientationState);
|
||||||
|
} else if (needGroupTaskView) {
|
||||||
|
taskView = getTaskViewFromPool(TaskView.Type.GROUPED);
|
||||||
mTmpRunningTasks = new Task[]{runningTasks[0], runningTasks[1]};
|
mTmpRunningTasks = new Task[]{runningTasks[0], runningTasks[1]};
|
||||||
addView(taskView, 0);
|
addView(taskView, 0);
|
||||||
// When we create a placeholder task view mSplitBoundsConfig will be null, but with
|
// When we create a placeholder task view mSplitBoundsConfig will be null, but with
|
||||||
@@ -2332,7 +2359,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
|||||||
((GroupedTaskView)taskView).bind(mTmpRunningTasks[0], mTmpRunningTasks[1],
|
((GroupedTaskView)taskView).bind(mTmpRunningTasks[0], mTmpRunningTasks[1],
|
||||||
mOrientationState, mSplitBoundsConfig);
|
mOrientationState, mSplitBoundsConfig);
|
||||||
} else {
|
} else {
|
||||||
taskView = getTaskViewFromPool(false);
|
taskView = getTaskViewFromPool(TaskView.Type.SINGLE);
|
||||||
addView(taskView, 0);
|
addView(taskView, 0);
|
||||||
// The temporary running task is only used for the duration between the start of the
|
// The temporary running task is only used for the duration between the start of the
|
||||||
// gesture and the task list is loaded and applied
|
// gesture and the task list is loaded and applied
|
||||||
@@ -2367,6 +2394,18 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
|||||||
reloadIfNeeded();
|
reloadIfNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean hasDesktopTask(Task[] runningTasks) {
|
||||||
|
if (!DESKTOP_MODE_SUPPORTED) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (Task task : runningTasks) {
|
||||||
|
if (task.key.windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the running task id, cleaning up the old running task if necessary.
|
* Sets the running task id, cleaning up the old running task if necessary.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -86,7 +86,6 @@ import com.android.launcher3.util.ViewPool.Reusable;
|
|||||||
import com.android.quickstep.RecentsModel;
|
import com.android.quickstep.RecentsModel;
|
||||||
import com.android.quickstep.RemoteAnimationTargets;
|
import com.android.quickstep.RemoteAnimationTargets;
|
||||||
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
|
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
|
||||||
import com.android.quickstep.SystemUiProxy;
|
|
||||||
import com.android.quickstep.TaskIconCache;
|
import com.android.quickstep.TaskIconCache;
|
||||||
import com.android.quickstep.TaskOverlayFactory;
|
import com.android.quickstep.TaskOverlayFactory;
|
||||||
import com.android.quickstep.TaskThumbnailCache;
|
import com.android.quickstep.TaskThumbnailCache;
|
||||||
@@ -135,6 +134,17 @@ public class TaskView extends FrameLayout implements Reusable {
|
|||||||
@IntDef({FLAG_UPDATE_ALL, FLAG_UPDATE_ICON, FLAG_UPDATE_THUMBNAIL})
|
@IntDef({FLAG_UPDATE_ALL, FLAG_UPDATE_ICON, FLAG_UPDATE_THUMBNAIL})
|
||||||
public @interface TaskDataChanges {}
|
public @interface TaskDataChanges {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of task view
|
||||||
|
*/
|
||||||
|
@Retention(SOURCE)
|
||||||
|
@IntDef({Type.SINGLE, Type.GROUPED, Type.DESKTOP})
|
||||||
|
public @interface Type {
|
||||||
|
int SINGLE = 1;
|
||||||
|
int GROUPED = 2;
|
||||||
|
int DESKTOP = 3;
|
||||||
|
}
|
||||||
|
|
||||||
/** The maximum amount that a task view can be scrimmed, dimmed or tinted. */
|
/** The maximum amount that a task view can be scrimmed, dimmed or tinted. */
|
||||||
public static final float MAX_PAGE_SCRIM_ALPHA = 0.4f;
|
public static final float MAX_PAGE_SCRIM_ALPHA = 0.4f;
|
||||||
|
|
||||||
@@ -331,7 +341,7 @@ public class TaskView extends FrameLayout implements Reusable {
|
|||||||
protected TaskThumbnailView mSnapshotView;
|
protected TaskThumbnailView mSnapshotView;
|
||||||
protected IconView mIconView;
|
protected IconView mIconView;
|
||||||
protected final DigitalWellBeingToast mDigitalWellBeingToast;
|
protected final DigitalWellBeingToast mDigitalWellBeingToast;
|
||||||
private float mFullscreenProgress;
|
protected float mFullscreenProgress;
|
||||||
private float mGridProgress;
|
private float mGridProgress;
|
||||||
protected float mTaskThumbnailSplashAlpha;
|
protected float mTaskThumbnailSplashAlpha;
|
||||||
private float mNonGridScale = 1;
|
private float mNonGridScale = 1;
|
||||||
@@ -373,8 +383,8 @@ public class TaskView extends FrameLayout implements Reusable {
|
|||||||
/**
|
/**
|
||||||
* Index 0 will contain taskID of left/top task, index 1 will contain taskId of bottom/right
|
* Index 0 will contain taskID of left/top task, index 1 will contain taskId of bottom/right
|
||||||
*/
|
*/
|
||||||
protected final int[] mTaskIdContainer = new int[]{-1, -1};
|
protected int[] mTaskIdContainer = new int[]{-1, -1};
|
||||||
protected final TaskIdAttributeContainer[] mTaskIdAttributeContainer =
|
protected TaskIdAttributeContainer[] mTaskIdAttributeContainer =
|
||||||
new TaskIdAttributeContainer[2];
|
new TaskIdAttributeContainer[2];
|
||||||
|
|
||||||
private boolean mShowScreenshot;
|
private boolean mShowScreenshot;
|
||||||
@@ -524,6 +534,13 @@ public class TaskView extends FrameLayout implements Reusable {
|
|||||||
return mTask;
|
return mTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if given {@code taskId} is tracked in this view
|
||||||
|
*/
|
||||||
|
public boolean containsTaskId(int taskId) {
|
||||||
|
return mTask != null && mTask.key.id == taskId;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return integer array of two elements to be size consistent with max number of tasks possible
|
* @return integer array of two elements to be size consistent with max number of tasks possible
|
||||||
* index 0 will contain the taskId, index 1 will be -1 indicating a null taskID value
|
* index 0 will contain the taskId, index 1 will be -1 indicating a null taskID value
|
||||||
@@ -564,13 +581,13 @@ public class TaskView extends FrameLayout implements Reusable {
|
|||||||
@Override
|
@Override
|
||||||
public boolean dispatchTouchEvent(MotionEvent ev) {
|
public boolean dispatchTouchEvent(MotionEvent ev) {
|
||||||
RecentsView recentsView = getRecentsView();
|
RecentsView recentsView = getRecentsView();
|
||||||
if (recentsView == null || mTask == null) {
|
if (recentsView == null || getTask() == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
SplitSelectStateController splitSelectStateController =
|
SplitSelectStateController splitSelectStateController =
|
||||||
recentsView.getSplitSelectController();
|
recentsView.getSplitSelectController();
|
||||||
if (splitSelectStateController.isSplitSelectActive() &&
|
if (splitSelectStateController.isSplitSelectActive() &&
|
||||||
splitSelectStateController.getInitialTaskId() == mTask.key.id) {
|
splitSelectStateController.getInitialTaskId() == getTask().key.id) {
|
||||||
// Prevent taps on the this taskview if it's being animated into split select state
|
// Prevent taps on the this taskview if it's being animated into split select state
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -597,11 +614,14 @@ public class TaskView extends FrameLayout implements Reusable {
|
|||||||
* @return {@code true} if user is already in split select mode and this tap was to choose the
|
* @return {@code true} if user is already in split select mode and this tap was to choose the
|
||||||
* second app. {@code false} otherwise
|
* second app. {@code false} otherwise
|
||||||
*/
|
*/
|
||||||
private boolean confirmSecondSplitSelectApp() {
|
protected boolean confirmSecondSplitSelectApp() {
|
||||||
int index = getLastSelectedChildTaskIndex();
|
int index = getLastSelectedChildTaskIndex();
|
||||||
TaskIdAttributeContainer container = mTaskIdAttributeContainer[index];
|
TaskIdAttributeContainer container = mTaskIdAttributeContainer[index];
|
||||||
return getRecentsView().confirmSplitSelect(this, container.getTask(),
|
if (container != null) {
|
||||||
container.getIconView(), container.getThumbnailView());
|
return getRecentsView().confirmSplitSelect(this, container.getTask(),
|
||||||
|
container.getIconView(), container.getThumbnailView());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -707,11 +727,6 @@ public class TaskView extends FrameLayout implements Reusable {
|
|||||||
RecentsView recentsView = getRecentsView();
|
RecentsView recentsView = getRecentsView();
|
||||||
RemoteTargetHandle[] remoteTargetHandles = recentsView.mRemoteTargetHandles;
|
RemoteTargetHandle[] remoteTargetHandles = recentsView.mRemoteTargetHandles;
|
||||||
RunnableList runnableList = new RunnableList();
|
RunnableList runnableList = new RunnableList();
|
||||||
if (mTask != null && mTask.desktopTile) {
|
|
||||||
// clicked on desktop
|
|
||||||
SystemUiProxy.INSTANCE.get(getContext()).showDesktopApps();
|
|
||||||
return runnableList;
|
|
||||||
}
|
|
||||||
if (isRunningTask() && remoteTargetHandles != null) {
|
if (isRunningTask() && remoteTargetHandles != null) {
|
||||||
if (!mIsClickableAsLiveTile) {
|
if (!mIsClickableAsLiveTile) {
|
||||||
return runnableList;
|
return runnableList;
|
||||||
|
|||||||
@@ -350,9 +350,11 @@ public class ItemInfo {
|
|||||||
break;
|
break;
|
||||||
case ITEM_TYPE_TASK:
|
case ITEM_TYPE_TASK:
|
||||||
itemBuilder
|
itemBuilder
|
||||||
.setTask(LauncherAtom.Task.newBuilder()
|
.setTask(nullableComponent
|
||||||
.setComponentName(getTargetComponent().flattenToShortString())
|
.map(component -> LauncherAtom.Task.newBuilder()
|
||||||
.setIndex(screenId));
|
.setComponentName(component.flattenToShortString())
|
||||||
|
.setIndex(screenId))
|
||||||
|
.orElse(LauncherAtom.Task.newBuilder()));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|||||||
Reference in New Issue
Block a user