Snap for 10515480 from 80fddb6e40 to udc-qpr1-release

Change-Id: I498b2ba1a9c3aaf63d797df18cfbf6652ece7868
This commit is contained in:
Android Build Coastguard Worker
2023-07-18 23:18:59 +00:00
16 changed files with 289 additions and 99 deletions
@@ -24,12 +24,15 @@
android:paddingTop="@dimen/split_instructions_vertical_padding"
android:paddingBottom="@dimen/split_instructions_vertical_padding"
android:elevation="@dimen/split_instructions_elevation"
android:visibility="gone">
android:visibility="gone"
android:importantForAccessibility="yes">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/split_instructions_text"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:gravity="center"
android:textColor="?androidprv:attr/textColorOnAccent"
android:drawableEnd="@drawable/ic_split_horizontal"
android:drawablePadding="@dimen/split_instructions_drawable_padding"
android:text="@string/toast_split_select_app" />
</com.android.quickstep.views.SplitInstructionsView>
+1
View File
@@ -230,6 +230,7 @@
<string name="action_split">Split</string>
<!-- Label for toast with instructions for split screen selection mode. [CHAR_LIMIT=50] -->
<string name="toast_split_select_app">Tap another app to use split screen</string>
<string name="toast_split_select_cont_desc">Exit split screen selection</string>
<!-- Label for toast when app selected for split isn't supported. [CHAR_LIMIT=50] -->
<string name="toast_split_app_unsupported">Choose another app to use split screen</string>
<!-- Message shown when an action is blocked by a policy enforced by the app or the organization managing the device. [CHAR_LIMIT=NONE] -->
@@ -544,17 +544,9 @@ public class QuickstepLauncher extends Launcher {
ArrayList<TouchController> list = new ArrayList<>();
list.add(getDragController());
Consumer<AnimatorSet> splitAnimator = animatorSet -> {
AnimatorSet anim = mSplitSelectStateController.getSplitAnimationController()
.createPlaceholderDismissAnim(QuickstepLauncher.this);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mSplitSelectStateController.resetState();
}
});
animatorSet.play(anim);
};
Consumer<AnimatorSet> splitAnimator = animatorSet ->
animatorSet.play(mSplitSelectStateController.getSplitAnimationController()
.createPlaceholderDismissAnim(this));
switch (mode) {
case NO_BUTTON:
list.add(new NoButtonQuickSwitchTouchController(this));
@@ -673,6 +665,8 @@ public class QuickstepLauncher extends Launcher {
mSplitSelectStateController.resetState();
}
});
anim.add(mSplitSelectStateController.getSplitAnimationController()
.getShowSplitInstructionsAnim(this).buildAnim());
anim.buildAnim().start();
}
@@ -134,6 +134,7 @@ import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.views.DesktopTaskView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.quickstep.views.TaskView.TaskIdAttributeContainer;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -150,6 +151,7 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.function.Consumer;
/**
@@ -656,11 +658,12 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
protected void notifyGestureAnimationStartToRecents() {
Task[] runningTasks;
TopTaskTracker.CachedTaskInfo cachedTaskInfo = mGestureState.getRunningTask();
if (mIsSwipeForSplit) {
int[] splitTaskIds = TopTaskTracker.INSTANCE.get(mContext).getRunningSplitTaskIds();
runningTasks = mGestureState.getRunningTask().getPlaceholderTasks(splitTaskIds);
runningTasks = cachedTaskInfo.getPlaceholderTasks(splitTaskIds);
} else {
runningTasks = mGestureState.getRunningTask().getPlaceholderTasks();
runningTasks = cachedTaskInfo.getPlaceholderTasks();
}
// Safeguard against any null tasks being sent to recents view, happens when quickswitching
@@ -733,8 +736,11 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
|| mRecentsView == null) {
return;
}
// looking at single target is fine here since either app of a split pair would
// have their "isInRecents" field set? (that's what this is used for below)
RemoteAnimationTarget runningTaskTarget = mRecentsAnimationTargets != null
? mRecentsAnimationTargets.findTask(mGestureState.getRunningTaskId())
? mRecentsAnimationTargets
.findTask(mGestureState.getTopRunningTaskId())
: null;
final boolean recentsAttachedToAppWindow;
if (mIsInAllAppsRegion) {
@@ -1190,7 +1196,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
return false;
}
boolean hasStartedTaskBefore = Arrays.stream(appearedTaskTarget).anyMatch(
targetCompat -> targetCompat.taskId == mGestureState.getLastStartedTaskId());
mGestureState.mLastStartedTaskIdPredicate);
if (mStateCallback.hasStates(STATE_START_NEW_TASK) && hasStartedTaskBefore) {
reset();
return true;
@@ -1456,9 +1462,12 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
@Override
public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
if (task.taskId == mGestureState.getRunningTaskId()
&& task.configuration.windowConfiguration.getActivityType()
!= ACTIVITY_TYPE_HOME) {
boolean taskRunningAndNotHome = Arrays.stream(mGestureState
.getRunningTaskIds(true /*getMultipleTasks*/))
.anyMatch(taskId -> task.taskId == taskId
&& task.configuration.windowConfiguration.getActivityType()
!= ACTIVITY_TYPE_HOME);
if (taskRunningAndNotHome) {
// Since this is an edge case, just cancel and relaunch with default activity
// options (since we don't know if there's an associated app icon to launch from)
endRunningWindowAnim(true /* cancel */);
@@ -1500,8 +1509,12 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
if (mGestureState.getEndTarget() == HOME) {
getOrientationHandler().adjustFloatingIconStartVelocity(velocityPxPerMs);
// Take first task ID, if there are multiple we don't have any special home
// animation so doesn't matter for splitscreen.. though the "allowEnterPip" might change
// depending on which task it is..
final RemoteAnimationTarget runningTaskTarget = mRecentsAnimationTargets != null
? mRecentsAnimationTargets.findTask(mGestureState.getRunningTaskId())
? mRecentsAnimationTargets
.findTask(mGestureState.getTopRunningTaskId())
: null;
final ArrayList<IBinder> cookies = runningTaskTarget != null
? runningTaskTarget.taskInfo.launchCookies
@@ -1530,7 +1543,8 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
// grab a screenshot before the PipContentOverlay gets parented on top of the task
UI_HELPER_EXECUTOR.execute(() -> {
final int taskId = mGestureState.getRunningTaskId();
// Directly use top task, split to pip handled on shell side
final int taskId = mGestureState.getTopRunningTaskId();
mTaskSnapshotCache.put(taskId,
mRecentsAnimationController.screenshotTask(taskId));
});
@@ -1994,13 +2008,10 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
// If there are no targets, then we don't need to capture anything
mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
} else {
final int runningTaskId = mGestureState.getRunningTaskId();
boolean finishTransitionPosted = false;
// If we already have cached screenshot(s) from running tasks, skip update
boolean shouldUpdate = false;
int[] runningTaskIds = mIsSwipeForSplit
? TopTaskTracker.INSTANCE.get(mContext).getRunningSplitTaskIds()
: new int[]{runningTaskId};
int[] runningTaskIds = mGestureState.getRunningTaskIds(mIsSwipeForSplit);
for (int id : runningTaskIds) {
if (!mTaskSnapshotCache.containsKey(id)) {
shouldUpdate = true;
@@ -2205,16 +2216,27 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
if (!mCanceled) {
TaskView nextTask = mRecentsView == null ? null : mRecentsView.getNextPageTaskView();
if (nextTask != null) {
Task.TaskKey nextTaskKey = nextTask.getTask().key;
int taskId = nextTaskKey.id;
mGestureState.updateLastStartedTaskId(taskId);
boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds()
.contains(taskId);
int[] taskIds = nextTask.getTaskIds();
StringBuilder nextTaskLog = new StringBuilder();
for (TaskIdAttributeContainer c : nextTask.getTaskIdAttributeContainers()) {
if (c == null) {
continue;
}
nextTaskLog
.append("[id: ")
.append(c.getTask().key.id)
.append(", pkg: ")
.append(c.getTask().key.getPackageName())
.append("] | ");
}
mGestureState.updateLastStartedTaskIds(taskIds);
boolean hasTaskPreviouslyAppeared = Arrays.stream(taskIds).anyMatch(
taskId -> mGestureState.getPreviouslyAppearedTaskIds()
.contains(taskId));
if (!hasTaskPreviouslyAppeared) {
ActiveGestureLog.INSTANCE.trackEvent(EXPECTING_TASK_APPEARED);
}
ActiveGestureLog.INSTANCE.addLog("Launching task: id=" + taskId
+ " pkg=" + nextTaskKey.getPackageName());
ActiveGestureLog.INSTANCE.addLog("Launching task: " + nextTaskLog);
nextTask.launchTask(success -> {
resultCallback.accept(success);
if (success) {
@@ -2284,7 +2306,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
public void onTasksAppeared(RemoteAnimationTarget[] appearedTaskTargets) {
if (mRecentsAnimationController != null) {
boolean hasStartedTaskBefore = Arrays.stream(appearedTaskTargets).anyMatch(
targetCompat -> targetCompat.taskId == mGestureState.getLastStartedTaskId());
mGestureState.mLastStartedTaskIdPredicate);
if (!mStateCallback.hasStates(STATE_GESTURE_COMPLETED) && !hasStartedTaskBefore) {
// This is a special case, if a task is started mid-gesture that wasn't a part of a
// previous quickswitch task launch, then cancel the animation back to the app
@@ -2297,8 +2319,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
} else if (handleTaskAppeared(appearedTaskTargets)) {
Optional<RemoteAnimationTarget> taskTargetOptional =
Arrays.stream(appearedTaskTargets)
.filter(targetCompat ->
targetCompat.taskId == mGestureState.getLastStartedTaskId())
.filter(mGestureState.mLastStartedTaskIdPredicate)
.findFirst();
if (!taskTargetOptional.isPresent()) {
ActiveGestureLog.INSTANCE.addLog("No appeared task matching started task id");
@@ -2367,11 +2388,16 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
* resume if we finish the controller.
*/
protected int getLastAppearedTaskIndex() {
return mRecentsView == null
? -1
: mGestureState.getLastAppearedTaskId() != -1
? mRecentsView.getTaskIndexForId(mGestureState.getLastAppearedTaskId())
: mRecentsView.getRunningTaskIndex();
if (mRecentsView == null) {
return -1;
}
OptionalInt firstValidTaskId = Arrays.stream(mGestureState.getLastAppearedTaskIds())
.filter(i -> i != -1)
.findFirst();
return firstValidTaskId.isPresent()
? mRecentsView.getTaskIndexForId(firstValidTaskId.getAsInt())
: mRecentsView.getRunningTaskIndex();
}
/**
@@ -2379,7 +2405,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
* but before that task appeared.
*/
protected boolean hasStartedNewTask() {
return mGestureState.getLastStartedTaskId() != -1;
return mGestureState.getLastStartedTaskIds()[0] != -1;
}
/**
@@ -15,8 +15,9 @@
*/
package com.android.quickstep;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static com.android.launcher3.MotionEventsUtils.isTrackpadFourFingerSwipe;
import static com.android.launcher3.MotionEventsUtils.isTrackpadMultiFingerSwipe;
import static com.android.launcher3.MotionEventsUtils.isTrackpadThreeFingerSwipe;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_ALLAPPS;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
@@ -44,10 +45,12 @@ import com.android.systemui.shared.recents.model.ThumbnailData;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
/**
* Manages the state for an active system gesture, listens for events from the system and Launcher,
@@ -56,6 +59,18 @@ import java.util.Set;
@TargetApi(Build.VERSION_CODES.R)
public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationListener {
final Predicate<RemoteAnimationTarget> mLastStartedTaskIdPredicate = new Predicate<>() {
@Override
public boolean test(RemoteAnimationTarget targetCompat) {
for (int taskId : mLastStartedTaskId) {
if (targetCompat.taskId == taskId) {
return true;
}
}
return false;
}
};
/**
* Defines the end targets of a gesture and the associated state.
*/
@@ -161,9 +176,9 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
private TrackpadGestureType mTrackpadGestureType = TrackpadGestureType.NONE;
private CachedTaskInfo mRunningTask;
private GestureEndTarget mEndTarget;
private RemoteAnimationTarget mLastAppearedTaskTarget;
private RemoteAnimationTarget[] mLastAppearedTaskTargets;
private Set<Integer> mPreviouslyAppearedTaskIds = new HashSet<>();
private int mLastStartedTaskId = -1;
private int[] mLastStartedTaskId = new int[]{INVALID_TASK_ID, INVALID_TASK_ID};
private RecentsAnimationController mRecentsAnimationController;
private HashMap<Integer, ThumbnailData> mRecentsAnimationCanceledSnapshots;
@@ -189,7 +204,7 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
mGestureId = other.mGestureId;
mRunningTask = other.mRunningTask;
mEndTarget = other.mEndTarget;
mLastAppearedTaskTarget = other.mLastAppearedTaskTarget;
mLastAppearedTaskTargets = other.mLastAppearedTaskTargets;
mPreviouslyAppearedTaskIds = other.mPreviouslyAppearedTaskIds;
mLastStartedTaskId = other.mLastStartedTaskId;
}
@@ -293,10 +308,29 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
}
/**
* @return the running task id for this gesture.
* @param getMultipleTasks Whether multiple tasks or not are to be returned (for split)
* @return the running task ids for this gesture.
*/
public int getRunningTaskId() {
return mRunningTask != null ? mRunningTask.getTaskId() : -1;
public int[] getRunningTaskIds(boolean getMultipleTasks) {
if (mRunningTask == null) {
return new int[]{INVALID_TASK_ID, INVALID_TASK_ID};
} else {
int cachedTasksSize = mRunningTask.mAllCachedTasks.size();
int count = Math.min(cachedTasksSize, getMultipleTasks ? 2 : 1);
int[] runningTaskIds = new int[count];
for (int i = 0; i < count; i++) {
runningTaskIds[i] = mRunningTask.mAllCachedTasks.get(i).taskId;
}
return runningTaskIds;
}
}
/**
* @see #getRunningTaskIds(boolean)
* @return the single top-most running taskId for this gesture
*/
public int getTopRunningTaskId() {
return getRunningTaskIds(false /*getMultipleTasks*/)[0];
}
/**
@@ -309,18 +343,26 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
/**
* Updates the last task that appeared during this gesture.
*/
public void updateLastAppearedTaskTarget(RemoteAnimationTarget lastAppearedTaskTarget) {
mLastAppearedTaskTarget = lastAppearedTaskTarget;
if (lastAppearedTaskTarget != null) {
mPreviouslyAppearedTaskIds.add(lastAppearedTaskTarget.taskId);
public void updateLastAppearedTaskTargets(RemoteAnimationTarget[] lastAppearedTaskTargets) {
mLastAppearedTaskTargets = lastAppearedTaskTargets;
for (RemoteAnimationTarget target : lastAppearedTaskTargets) {
if (target == null) {
continue;
}
mPreviouslyAppearedTaskIds.add(target.taskId);
}
}
/**
* @return The id of the task that appeared during this gesture.
*/
public int getLastAppearedTaskId() {
return mLastAppearedTaskTarget != null ? mLastAppearedTaskTarget.taskId : -1;
public int[] getLastAppearedTaskIds() {
if (mLastAppearedTaskTargets == null) {
return new int[]{INVALID_TASK_ID, INVALID_TASK_ID};
} else {
return Arrays.stream(mLastAppearedTaskTargets)
.mapToInt(target -> target != null ? target.taskId : INVALID_TASK_ID).toArray();
}
}
public void updatePreviouslyAppearedTaskIds(Set<Integer> previouslyAppearedTaskIds) {
@@ -334,7 +376,7 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
/**
* Updates the last task that we started via startActivityFromRecents() during this gesture.
*/
public void updateLastStartedTaskId(int lastStartedTaskId) {
public void updateLastStartedTaskIds(int[] lastStartedTaskId) {
mLastStartedTaskId = lastStartedTaskId;
}
@@ -342,7 +384,7 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
* @return The id of the task that was most recently started during this gesture, or -1 if
* no task has been started yet (i.e. we haven't settled on a new task).
*/
public int getLastStartedTaskId() {
public int[] getLastStartedTaskIds() {
return mLastStartedTaskId;
}
@@ -478,8 +520,8 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
pw.println(" gestureID=" + mGestureId);
pw.println(" runningTask=" + mRunningTask);
pw.println(" endTarget=" + mEndTarget);
pw.println(" lastAppearedTaskTargetId=" + getLastAppearedTaskId());
pw.println(" lastStartedTaskId=" + mLastStartedTaskId);
pw.println(" lastAppearedTaskTargetId=" + Arrays.toString(mLastAppearedTaskTargets));
pw.println(" lastStartedTaskId=" + Arrays.toString(mLastStartedTaskId));
pw.println(" isRecentsAnimationRunning=" + isRecentsAnimationRunning());
}
}
@@ -45,6 +45,7 @@ import android.view.View;
import android.window.RemoteTransition;
import android.window.SplashScreen;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.app.animation.Interpolators;
@@ -240,6 +241,11 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> {
}
final TaskView taskView = (TaskView) v;
final RecentsView recentsView = taskView.getRecentsView();
if (recentsView == null) {
return super.getActivityLaunchOptions(v, item);
}
RunnableList onEndCallback = new RunnableList();
mActivityLaunchAnimationRunner = new RemoteAnimationFactory() {
@@ -248,7 +254,7 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> {
RemoteAnimationTarget[] wallpaperTargets,
RemoteAnimationTarget[] nonAppTargets, AnimationResult result) {
mHandler.removeCallbacks(mAnimationStartTimeoutRunnable);
AnimatorSet anim = composeRecentsLaunchAnimator(taskView, appTargets,
AnimatorSet anim = composeRecentsLaunchAnimator(recentsView, taskView, appTargets,
wallpaperTargets, nonAppTargets);
anim.addListener(resetStateListener());
result.setAnimation(anim, RecentsActivity.this, onEndCallback::executeAllAndDestroy,
@@ -285,14 +291,16 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> {
/**
* Composes the animations for a launch from the recents list if possible.
*/
private AnimatorSet composeRecentsLaunchAnimator(TaskView taskView,
private AnimatorSet composeRecentsLaunchAnimator(
@NonNull RecentsView recentsView,
@NonNull TaskView taskView,
RemoteAnimationTarget[] appTargets,
RemoteAnimationTarget[] wallpaperTargets,
RemoteAnimationTarget[] nonAppTargets) {
AnimatorSet target = new AnimatorSet();
boolean activityClosing = taskIsATargetWithMode(appTargets, getTaskId(), MODE_CLOSING);
PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION);
createRecentsWindowAnimator(taskView, !activityClosing, appTargets,
createRecentsWindowAnimator(recentsView, taskView, !activityClosing, appTargets,
wallpaperTargets, nonAppTargets, null /* depthController */, pa);
target.play(pa.buildAnim());
@@ -115,8 +115,8 @@ public class RecentsAnimationController {
* {@link RecentsAnimationCallbacks#onTasksAppeared}}.
*/
@UiThread
public void removeTaskTarget(@NonNull RemoteAnimationTarget target) {
UI_HELPER_EXECUTOR.execute(() -> mController.removeTask(target.taskId));
public void removeTaskTarget(int targetTaskId) {
UI_HELPER_EXECUTOR.execute(() -> mController.removeTask(targetTaskId));
}
@UiThread
@@ -167,7 +167,6 @@ public class RecentsAnimationController {
/* event= */ "finishRecentsAnimation",
/* extras= */ toRecents,
/* gestureEvent= */ FINISH_RECENTS_ANIMATION);
// Finish not yet requested
mFinishRequested = true;
mFinishTargetIsLauncher = toRecents;
@@ -59,7 +59,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
private RecentsAnimationTargets mTargets;
// Temporary until we can hook into gesture state events
private GestureState mLastGestureState;
private RemoteAnimationTarget mLastAppearedTaskTarget;
private RemoteAnimationTarget[] mLastAppearedTaskTargets;
private Runnable mLiveTileCleanUpHandler;
private Context mCtx;
@@ -141,8 +141,15 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
}
mController = controller;
mTargets = targets;
mLastAppearedTaskTarget = mTargets.findTask(mLastGestureState.getRunningTaskId());
mLastGestureState.updateLastAppearedTaskTarget(mLastAppearedTaskTarget);
// TODO(b/236226779): We can probably get away w/ setting mLastAppearedTaskTargets
// to all appeared targets directly vs just looking at running ones
int[] runningTaskIds = mLastGestureState.getRunningTaskIds(targets.apps.length > 1);
mLastAppearedTaskTargets = new RemoteAnimationTarget[runningTaskIds.length];
for (int i = 0; i < runningTaskIds.length; i++) {
RemoteAnimationTarget task = mTargets.findTask(runningTaskIds[i]);
mLastAppearedTaskTargets[i] = task;
}
mLastGestureState.updateLastAppearedTaskTargets(mLastAppearedTaskTargets);
}
@Override
@@ -196,14 +203,17 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
true /*shown*/, null /* animatorHandler */);
}
if (mController != null) {
if (mLastAppearedTaskTarget == null
|| appearedTaskTarget.taskId != mLastAppearedTaskTarget.taskId) {
if (mLastAppearedTaskTarget != null) {
mController.removeTaskTarget(mLastAppearedTaskTarget);
if (mLastAppearedTaskTargets != null) {
for (RemoteAnimationTarget lastTarget : mLastAppearedTaskTargets) {
for (RemoteAnimationTarget appearedTarget : appearedTaskTargets) {
if (appearedTarget.taskId != lastTarget.taskId) {
mController.removeTaskTarget(lastTarget.taskId);
}
}
}
mLastAppearedTaskTarget = appearedTaskTarget;
mLastGestureState.updateLastAppearedTaskTarget(mLastAppearedTaskTarget);
}
mLastAppearedTaskTargets = appearedTaskTargets;
mLastGestureState.updateLastAppearedTaskTargets(mLastAppearedTaskTargets);
}
}
@@ -268,7 +278,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
mCallbacks.addListener(gestureState);
gestureState.setState(STATE_RECENTS_ANIMATION_INITIALIZED
| STATE_RECENTS_ANIMATION_STARTED);
gestureState.updateLastAppearedTaskTarget(mLastAppearedTaskTarget);
gestureState.updateLastAppearedTaskTargets(mLastAppearedTaskTargets);
return mCallbacks;
}
@@ -369,7 +379,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
mCallbacks = null;
mTargets = null;
mLastGestureState = null;
mLastAppearedTaskTarget = null;
mLastAppearedTaskTargets = null;
}
@Nullable
@@ -160,13 +160,14 @@ public final class TaskViewUtils {
}
public static void createRecentsWindowAnimator(
@NonNull TaskView v, boolean skipViewChanges,
@NonNull RecentsView recentsView,
@NonNull TaskView v,
boolean skipViewChanges,
@NonNull RemoteAnimationTarget[] appTargets,
@NonNull RemoteAnimationTarget[] wallpaperTargets,
@NonNull RemoteAnimationTarget[] nonAppTargets,
@Nullable DepthController depthController,
PendingAnimation out) {
RecentsView recentsView = v.getRecentsView();
boolean isQuickSwitch = v.isEndQuickswitchCuj();
v.setEndQuickswitchCuj(false);
@@ -606,8 +607,8 @@ public final class TaskViewUtils {
TaskView taskView = findTaskViewToLaunch(recentsView, v, appTargets);
PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION);
createRecentsWindowAnimator(taskView, skipLauncherChanges, appTargets, wallpaperTargets,
nonAppTargets, depthController, pa);
createRecentsWindowAnimator(recentsView, taskView, skipLauncherChanges, appTargets,
wallpaperTargets, nonAppTargets, depthController, pa);
if (launcherClosing) {
// TODO(b/182592057): differentiate between "restore split" vs "launch fullscreen app"
TaskViewUtils.createSplitAuxiliarySurfacesAnimator(nonAppTargets, true /*shown*/,
@@ -210,7 +210,7 @@ public class TopTaskTracker extends ISplitScreenListener.Stub implements TaskSta
@Nullable
private final RunningTaskInfo mTopTask;
private final List<RunningTaskInfo> mAllCachedTasks;
public final List<RunningTaskInfo> mAllCachedTasks;
CachedTaskInfo(List<RunningTaskInfo> allCachedTasks) {
mAllCachedTasks = allCachedTasks;
@@ -41,7 +41,6 @@ import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNF
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BACK_ANIMATION;
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BUBBLES;
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_DESKTOP_MODE;
@@ -792,7 +791,7 @@ public class TouchInteractionService extends Service {
ActiveGestureLog.INSTANCE.getLogId());
taskInfo = previousGestureState.getRunningTask();
gestureState.updateRunningTask(taskInfo);
gestureState.updateLastStartedTaskId(previousGestureState.getLastStartedTaskId());
gestureState.updateLastStartedTaskIds(previousGestureState.getLastStartedTaskIds());
gestureState.updatePreviouslyAppearedTaskIds(
previousGestureState.getPreviouslyAppearedTaskIds());
} else {
@@ -1005,6 +1004,7 @@ public class TouchInteractionService extends Service {
}
reasonString.append(SUBSTRING_PREFIX).append("keyguard is not showing occluded");
// Use overview input consumer for sharesheets on top of home.
boolean forceOverviewInputConsumer = gestureState.getActivityInterface().isStarted()
&& gestureState.getRunningTask() != null
@@ -302,7 +302,7 @@ public class AllSetActivity extends Activity {
}
private AnimatedFloat createSwipeUpProxy(GestureState state) {
if (state.getRunningTaskId() != getTaskId()) {
if (state.getTopRunningTaskId() != getTaskId()) {
return null;
}
mSwipeProgress.updateValue(0);
@@ -26,15 +26,17 @@ import android.graphics.Rect
import android.graphics.RectF
import android.graphics.drawable.Drawable
import android.view.View
import com.android.app.animation.Interpolators
import com.android.launcher3.DeviceProfile
import com.android.launcher3.Launcher
import com.android.launcher3.Utilities
import com.android.launcher3.anim.PendingAnimation
import com.android.launcher3.dragndrop.DragLayer
import com.android.launcher3.statemanager.StatefulActivity
import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource
import com.android.launcher3.views.BaseDragLayer
import com.android.quickstep.views.FloatingTaskView
import com.android.quickstep.views.IconView
import com.android.quickstep.views.RecentsView
import com.android.quickstep.views.SplitInstructionsView
import com.android.quickstep.views.TaskThumbnailView
import com.android.quickstep.views.TaskView
import com.android.quickstep.views.TaskView.TaskIdAttributeContainer
@@ -58,6 +60,8 @@ class SplitAnimationController(val splitSelectStateController: SplitSelectStateC
)
}
var splitInstructionsView: SplitInstructionsView? = null
/**
* Returns different elements to animate for the initial split selection animation
* depending on the state of the surface from which the split was initiated
@@ -188,31 +192,26 @@ class SplitAnimationController(val splitSelectStateController: SplitSelectStateC
}
/** Does not play any animation if user is not currently in split selection state. */
fun playPlaceholderDismissAnim(launcher: Launcher) {
fun playPlaceholderDismissAnim(launcher: StatefulActivity<*>) {
if (!splitSelectStateController.isSplitSelectActive) {
return
}
val anim = createPlaceholderDismissAnim(launcher)
anim.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
splitSelectStateController.resetState()
}
})
anim.start()
}
/** Returns [AnimatorSet] which slides initial split placeholder view offscreen. */
fun createPlaceholderDismissAnim(launcher: Launcher) : AnimatorSet {
fun createPlaceholderDismissAnim(launcher: StatefulActivity<*>) : AnimatorSet {
val animatorSet = AnimatorSet()
val recentsView : RecentsView<*, *> = launcher.getOverviewPanel()
val floatingTask: FloatingTaskView = splitSelectStateController.firstFloatingTaskView
?: return animatorSet
// We are in split selection state currently, transitioning to another state
val dragLayer: DragLayer = launcher.dragLayer
val dragLayer: BaseDragLayer<*> = launcher.dragLayer
val onScreenRectF = RectF()
Utilities.getBoundsForViewInDragLayer(launcher.dragLayer, floatingTask,
Utilities.getBoundsForViewInDragLayer(dragLayer, floatingTask,
Rect(0, 0, floatingTask.width, floatingTask.height),
false, null, onScreenRectF)
// Get the part of the floatingTask that intersects with the DragLayer (i.e. the
@@ -233,6 +232,42 @@ class SplitAnimationController(val splitSelectStateController: SplitSelectStateC
floatingTask.stagePosition,
launcher.deviceProfile
)))
animatorSet.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
splitSelectStateController.resetState()
safeRemoveViewFromDragLayer(launcher, splitInstructionsView)
}
})
return animatorSet
}
/**
* Returns a [PendingAnimation] to animate in the chip to instruct a user to select a second
* app for splitscreen
*/
fun getShowSplitInstructionsAnim(launcher: StatefulActivity<*>) : PendingAnimation {
safeRemoveViewFromDragLayer(launcher, splitInstructionsView)
splitInstructionsView = SplitInstructionsView.getSplitInstructionsView(launcher)
val timings = AnimUtils.getDeviceOverviewToSplitTimings(launcher.deviceProfile.isTablet)
val anim = PendingAnimation(100 /*duration */)
anim.setViewAlpha(splitInstructionsView, 1f,
Interpolators.clampToProgress(Interpolators.LINEAR,
timings.instructionsContainerFadeInStartOffset,
timings.instructionsContainerFadeInEndOffset))
anim.setViewAlpha(splitInstructionsView!!.textView, 1f,
Interpolators.clampToProgress(Interpolators.LINEAR,
timings.instructionsTextFadeInStartOffset,
timings.instructionsTextFadeInEndOffset))
anim.addFloat(splitInstructionsView, SplitInstructionsView.UNFOLD, 0.1f, 1f,
Interpolators.clampToProgress(Interpolators.EMPHASIZED_DECELERATE,
timings.instructionsUnfoldStartOffset,
timings.instructionsUnfoldEndOffset))
return anim
}
private fun safeRemoveViewFromDragLayer(launcher: StatefulActivity<*>, view: View?) {
if (view != null) {
launcher.dragLayer.removeView(view)
}
}
}
@@ -5233,7 +5233,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
true /* forDesktop */);
mRemoteTargetHandles = gluer.assignTargetsForDesktop(recentsAnimationTargets);
} else {
gluer = new RemoteTargetGluer(getContext(), getSizeStrategy());
gluer = new RemoteTargetGluer(getContext(), getSizeStrategy(), recentsAnimationTargets,
false);
mRemoteTargetHandles = gluer.assignTargetsForSplitScreen(recentsAnimationTargets);
}
mSplitBoundsConfig = gluer.getSplitBounds();
@@ -17,15 +17,21 @@
package com.android.quickstep.views;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatTextView;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.statemanager.StatefulActivity;
/**
@@ -65,7 +71,7 @@ public class SplitInstructionsView extends FrameLayout {
mLauncher = (StatefulActivity) context;
}
static SplitInstructionsView getSplitInstructionsView(StatefulActivity launcher) {
public static SplitInstructionsView getSplitInstructionsView(StatefulActivity launcher) {
ViewGroup dragLayer = launcher.getDragLayer();
final SplitInstructionsView splitInstructionsView =
(SplitInstructionsView) launcher.getLayoutInflater().inflate(
@@ -73,9 +79,7 @@ public class SplitInstructionsView extends FrameLayout {
dragLayer,
false
);
splitInstructionsView.mTextView = splitInstructionsView.findViewById(
R.id.split_instructions_text);
splitInstructionsView.init();
// Since textview overlays base view, and we sometimes manipulate the alpha of each
// simultaneously, force overlapping rendering to false prevents redrawing of pixels,
@@ -92,6 +96,71 @@ public class SplitInstructionsView extends FrameLayout {
ensureProperRotation();
}
private void init() {
mTextView = findViewById(R.id.split_instructions_text);
if (!FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
mTextView.setCompoundDrawables(null, null, null, null);
return;
}
mTextView.setOnTouchListener((v, event) -> {
if (isTouchInsideRightCompoundDrawable(event)) {
if (event.getAction() == MotionEvent.ACTION_UP) {
exitSplitSelection();
}
return true;
}
return false;
});
}
private void exitSplitSelection() {
((RecentsView) mLauncher.getOverviewPanel()).getSplitSelectController()
.getSplitAnimationController().playPlaceholderDismissAnim(mLauncher);
mLauncher.getStateManager().goToState(LauncherState.NORMAL);
}
private boolean isTouchInsideRightCompoundDrawable(MotionEvent event) {
// Get the right compound drawable of the TextView.
Drawable rightDrawable = mTextView.getCompoundDrawablesRelative()[2];
// Check if the touch event intersects with the drawable's bounds.
if (rightDrawable != null) {
// We can get away w/o caring about the Y bounds since it's such a small view, if it's
// above/below the drawable just assume they meant to touch it. ¯\_()_/¯
return event.getX() >= (mTextView.getWidth() - rightDrawable.getBounds().width());
} else {
return false;
}
}
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
if (!FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
return;
}
info.addAction(new AccessibilityNodeInfo.AccessibilityAction(
R.string.toast_split_select_cont_desc,
getResources().getString(R.string.toast_split_select_cont_desc)
));
}
@Override
public boolean performAccessibilityAction(int action, Bundle arguments) {
if (!FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
return super.performAccessibilityAction(action, arguments);
}
if (action == R.string.toast_split_select_cont_desc) {
exitSplitSelection();
return true;
}
return super.performAccessibilityAction(action, arguments);
}
void ensureProperRotation() {
((RecentsView) mLauncher.getOverviewPanel()).getPagedOrientationHandler()
.setSplitInstructionsParams(
+1
View File
@@ -411,6 +411,7 @@
<dimen name="split_instructions_elevation">1dp</dimen>
<dimen name="split_instructions_horizontal_padding">24dp</dimen>
<dimen name="split_instructions_vertical_padding">12dp</dimen>
<dimen name="split_instructions_drawable_padding">10dp</dimen>
<dimen name="split_instructions_bottom_margin_phone_landscape">24dp</dimen>
<dimen name="split_instructions_bottom_margin_phone_portrait">60dp</dimen>