Merge "Add GroupedTaskView for gestures in staged split." into sc-v2-dev

This commit is contained in:
Vinit Nayak 2021-08-17 23:26:53 +00:00 committed by Android (Google) Code Review
commit ca1d523734
25 changed files with 1156 additions and 253 deletions

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?><!--
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.
-->
<!-- NOTE! don't add dimensions for margins / paddings / sizes that change per orientation to this
file, they need to be loaded at runtime. -->
<!-- DOUBLE NOTE! Don't deviate IDs from task.xml since this layout acts as a "subclass" (read as
"bad code"). How can we use the view pool in RecentsView to use task.xml layout with using
GroupedTaskView.java class? Is that possible (while still keeping code in separate class) ? -->
<com.android.quickstep.views.GroupedTaskView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:defaultFocusHighlightEnabled="false"
android:focusable="true">
<com.android.quickstep.views.TaskThumbnailView
android:id="@+id/snapshot"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<com.android.quickstep.views.TaskThumbnailView
android:id="@+id/bottomright_snapshot"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<com.android.quickstep.views.IconView
android:id="@+id/icon"
android:layout_width="@dimen/task_thumbnail_icon_size"
android:layout_height="@dimen/task_thumbnail_icon_size"
android:focusable="false"
android:importantForAccessibility="no"/>
</com.android.quickstep.views.GroupedTaskView>

View File

@ -63,7 +63,7 @@ public class RecentsActivityTest {
RunningTaskInfo placeholderTask = new RunningTaskInfo();
placeholderTask.taskId = 22;
frv.showCurrentTask(placeholderTask);
frv.showCurrentTask(new RunningTaskInfo[]{placeholderTask});
doLayout(activity);
ThumbnailData thumbnailData = new ThumbnailData();

View File

@ -99,6 +99,7 @@ import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.InputConsumerProxy;
import com.android.quickstep.util.InputProxyHandlerFactory;
import com.android.quickstep.util.LauncherSplitScreenListener;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.util.ProtoTracer;
import com.android.quickstep.util.RecentsOrientedState;
@ -106,6 +107,7 @@ import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.StaggeredWorkspaceAnim;
import com.android.quickstep.util.SurfaceTransactionApplier;
import com.android.quickstep.util.SwipePipToHomeAnimator;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
@ -222,7 +224,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
protected final TaskAnimationManager mTaskAnimationManager;
// Either RectFSpringAnim (if animating home) or ObjectAnimator (from mCurrentShift) otherwise
private RunningWindowAnim mRunningWindowAnim;
private RunningWindowAnim[] mRunningWindowAnim;
// Possible second animation running at the same time as mRunningWindowAnim
private Animator mParallelRunningAnim;
private boolean mIsMotionPaused;
@ -253,6 +255,10 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
private SwipePipToHomeAnimator mSwipePipToHomeAnimator;
protected boolean mIsSwipingPipToHome;
// TODO(b/195473090) no split PIP for now, remove once we have more clarity
// can try to have RectFSpringAnim evaluate multiple rects at once
private final SwipePipToHomeAnimator[] mSwipePipToHomeAnimators =
new SwipePipToHomeAnimator[2];
// Interpolate RecentsView scale from start of quick switch scroll until this scroll threshold
private final float mQuickSwitchScaleScrollThreshold;
@ -426,7 +432,8 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
// RecentsView never updates the display rotation until swipe-up, force update
// RecentsOrientedState before passing to TaskViewSimulator.
mRecentsView.updateRecentsRotation();
mTaskViewSimulator.setOrientationState(mRecentsView.getPagedViewOrientedState());
runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.mTaskViewSimulator
.setOrientationState(mRecentsView.getPagedViewOrientedState()));
// If we've already ended the gesture and are going home, don't prepare recents UI,
// as that will set the state as BACKGROUND_APP, overriding the animation to NORMAL.
@ -519,7 +526,21 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
}
protected void notifyGestureAnimationStartToRecents() {
mRecentsView.onGestureAnimationStart(mGestureState.getRunningTask());
ActivityManager.RunningTaskInfo[] runningTasks;
if (mIsSwipeForStagedSplit) {
int[] splitTaskIds =
LauncherSplitScreenListener.INSTANCE.getNoCreate().getSplitTaskIds();
runningTasks = new ActivityManager.RunningTaskInfo[splitTaskIds.length];
for (int i = 0; i < splitTaskIds.length; i++) {
int taskId = splitTaskIds[i];
ActivityManager.RunningTaskInfo rti = new ActivityManager.RunningTaskInfo();
rti.taskId = taskId;
runningTasks[i] = rti;
}
} else {
runningTasks = new ActivityManager.RunningTaskInfo[]{mGestureState.getRunningTask()};
}
mRecentsView.onGestureAnimationStart(runningTasks);
}
private void launcherFrameDrawn() {
@ -606,15 +627,15 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
if (animate) {
ValueAnimator reapplyWindowTransformAnim = ValueAnimator.ofFloat(0, 1);
reapplyWindowTransformAnim.addUpdateListener(anim -> {
if (mRunningWindowAnim == null) {
applyWindowTransform();
if (mRunningWindowAnim == null || mRunningWindowAnim.length == 0) {
applyScrollAndTransform();
}
});
reapplyWindowTransformAnim.setDuration(RECENTS_ATTACH_DURATION).start();
mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED,
reapplyWindowTransformAnim::cancel);
} else {
applyWindowTransform();
applyScrollAndTransform();
}
}
@ -678,7 +699,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
}
updateSysUiFlags(mCurrentShift.value);
applyWindowTransform();
applyScrollAndTransform();
updateLauncherTransitionProgress();
}
@ -724,24 +745,23 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
@Override
public void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targets) {
ActiveGestureLog.INSTANCE.addLog("startRecentsAnimationCallback", targets.apps.length);
super.onRecentsAnimationStart(controller, targets);
mRecentsAnimationController = controller;
mRecentsAnimationTargets = targets;
mTransformParams.setTargetSet(mRecentsAnimationTargets);
RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(
mGestureState.getRunningTaskId());
if (runningTaskTarget != null) {
mTaskViewSimulator.setPreview(runningTaskTarget);
}
// Only initialize the device profile, if it has not been initialized before, as in some
// configurations targets.homeContentInsets may not be correct.
if (mActivity == null) {
DeviceProfile dp = mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile();
if (targets.minimizedHomeBounds != null && runningTaskTarget != null) {
RemoteAnimationTargetCompat primaryTaskTarget = targets.apps[0];
// orientation state is independent of which remote target handle we use since both
// should be pointing to the same one. Just choose index 0 for now since that works for
// both split and non-split
RecentsOrientedState orientationState = mRemoteTargetHandles[0].mTaskViewSimulator
.getOrientationState();
DeviceProfile dp = orientationState.getLauncherDeviceProfile();
if (targets.minimizedHomeBounds != null && primaryTaskTarget != null) {
Rect overviewStackBounds = mActivityInterface
.getOverviewWindowBounds(targets.minimizedHomeBounds, runningTaskTarget);
.getOverviewWindowBounds(targets.minimizedHomeBounds, primaryTaskTarget);
dp = dp.getMultiWindowProfile(mContext,
new WindowBounds(overviewStackBounds, targets.homeContentInsets));
} else {
@ -751,7 +771,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
dp.updateInsets(targets.homeContentInsets);
dp.updateIsSeascape(mContext);
initTransitionEndpoints(dp);
mTaskViewSimulator.getOrientationState().setMultiWindowMode(dp.isMultiWindowMode);
orientationState.setMultiWindowMode(dp.isMultiWindowMode);
}
// Notify when the animation starts
@ -869,9 +889,17 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
private void endRunningWindowAnim(boolean cancel) {
if (mRunningWindowAnim != null) {
if (cancel) {
mRunningWindowAnim.cancel();
for (RunningWindowAnim r : mRunningWindowAnim) {
if (r != null) {
r.cancel();
}
}
} else {
mRunningWindowAnim.end();
for (RunningWindowAnim r : mRunningWindowAnim) {
if (r != null) {
r.end();
}
}
}
}
if (mParallelRunningAnim != null) {
@ -1181,15 +1209,17 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
createHomeAnimationFactory(cookies, duration, isTranslucent, appCanEnterPip,
runningTaskTarget);
mIsSwipingPipToHome = homeAnimFactory.supportSwipePipToHome() && appCanEnterPip;
final RectFSpringAnim windowAnim;
final RectFSpringAnim[] windowAnim;
if (mIsSwipingPipToHome) {
mSwipePipToHomeAnimator = createWindowAnimationToPip(
homeAnimFactory, runningTaskTarget, start);
windowAnim = mSwipePipToHomeAnimator;
mSwipePipToHomeAnimators[0] = mSwipePipToHomeAnimator;
windowAnim = mSwipePipToHomeAnimators;
} else {
mSwipePipToHomeAnimator = null;
windowAnim = createWindowAnimationToHome(start, homeAnimFactory);
windowAnim.addAnimatorListener(new AnimationSuccessListener() {
windowAnim[0].addAnimatorListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
if (mRecentsAnimationController == null) {
@ -1203,15 +1233,22 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
}
});
}
windowAnim.start(mContext, velocityPxPerMs);
mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim);
mRunningWindowAnim = new RunningWindowAnim[windowAnim.length];
for (int i = 0, windowAnimLength = windowAnim.length; i < windowAnimLength; i++) {
RectFSpringAnim windowAnimation = windowAnim[i];
if (windowAnimation == null) {
continue;
}
windowAnimation.start(mContext, velocityPxPerMs);
mRunningWindowAnim[i] = RunningWindowAnim.wrap(windowAnimation);
}
homeAnimFactory.setSwipeVelocity(velocityPxPerMs.y);
homeAnimFactory.playAtomicAnimation(velocityPxPerMs.y);
mLauncherTransitionController = null;
if (mRecentsView != null) {
mRecentsView.onPrepareGestureEndAnimation(null, mGestureState.getEndTarget(),
mTaskViewSimulator);
getRemoteTaskViewSimulators());
}
} else {
AnimatorSet animatorSet = new AnimatorSet();
@ -1253,11 +1290,12 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
animatorSet.play(windowAnim);
if (mRecentsView != null) {
mRecentsView.onPrepareGestureEndAnimation(
animatorSet, mGestureState.getEndTarget(), mTaskViewSimulator);
animatorSet, mGestureState.getEndTarget(),
getRemoteTaskViewSimulators());
}
animatorSet.setDuration(duration).setInterpolator(interpolator);
animatorSet.start();
mRunningWindowAnim = RunningWindowAnim.wrap(animatorSet);
mRunningWindowAnim = new RunningWindowAnim[]{RunningWindowAnim.wrap(animatorSet)};
}
}
@ -1272,16 +1310,21 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
}
}
/**
* TODO(b/195473090) handle multiple task simulators (if needed) for PIP
*/
private SwipePipToHomeAnimator createWindowAnimationToPip(HomeAnimationFactory homeAnimFactory,
RemoteAnimationTargetCompat runningTaskTarget, float startProgress) {
// Directly animate the app to PiP (picture-in-picture) mode
final ActivityManager.RunningTaskInfo taskInfo = mGestureState.getRunningTask();
final RecentsOrientedState orientationState = mTaskViewSimulator.getOrientationState();
final RecentsOrientedState orientationState = mRemoteTargetHandles[0].mTaskViewSimulator
.getOrientationState();
final int windowRotation = calculateWindowRotation(runningTaskTarget, orientationState);
final int homeRotation = orientationState.getRecentsActivityRotation();
final Matrix homeToWindowPositionMap = new Matrix();
final RectF startRect = updateProgressForStartRect(homeToWindowPositionMap, startProgress);
final RectF startRect = updateProgressForStartRect(homeToWindowPositionMap,
startProgress)[0];
// Move the startRect to Launcher space as floatingIconView runs in Launcher
final Matrix windowToHomePositionMap = new Matrix();
homeToWindowPositionMap.invert(windowToHomePositionMap);
@ -1310,7 +1353,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
// is not ROTATION_0 (which implies the rotation is turned on in launcher settings).
if (homeRotation == ROTATION_0
&& (windowRotation == ROTATION_90 || windowRotation == ROTATION_270)) {
builder.setFromRotation(mTaskViewSimulator, windowRotation,
builder.setFromRotation(mRemoteTargetHandles[0].mTaskViewSimulator, windowRotation,
taskInfo.displayCutoutInsets);
}
final SwipePipToHomeAnimator swipePipToHomeAnimator = builder.build();
@ -1340,7 +1383,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
}
});
setupWindowAnimation(swipePipToHomeAnimator);
setupWindowAnimation(new RectFSpringAnim[]{swipePipToHomeAnimator});
return swipePipToHomeAnimator;
}
@ -1367,19 +1410,19 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
* @param homeAnimationFactory The home animation factory.
*/
@Override
protected RectFSpringAnim createWindowAnimationToHome(float startProgress,
protected RectFSpringAnim[] createWindowAnimationToHome(float startProgress,
HomeAnimationFactory homeAnimationFactory) {
RectFSpringAnim anim =
RectFSpringAnim[] anim =
super.createWindowAnimationToHome(startProgress, homeAnimationFactory);
setupWindowAnimation(anim);
return anim;
}
private void setupWindowAnimation(RectFSpringAnim anim) {
anim.addOnUpdateListener((v, r, p) -> {
private void setupWindowAnimation(RectFSpringAnim[] anims) {
anims[0].addOnUpdateListener((v, r, p) -> {
updateSysUiFlags(Math.max(p, mCurrentShift.value));
});
anim.addAnimatorListener(new AnimationSuccessListener() {
anims[0].addAnimatorListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
if (mRecentsView != null) {
@ -1391,7 +1434,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
}
});
if (mRecentsAnimationTargets != null) {
mRecentsAnimationTargets.addReleaseCheck(anim);
mRecentsAnimationTargets.addReleaseCheck(anims[0]);
}
}
@ -1639,7 +1682,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
* if applicable. This should happen before {@link #finishRecentsControllerToHome(Runnable)}.
*/
private void maybeFinishSwipePipToHome() {
if (mIsSwipingPipToHome && mSwipePipToHomeAnimator != null) {
if (mIsSwipingPipToHome && mSwipePipToHomeAnimators[0] != null) {
SystemUiProxy.INSTANCE.get(mContext).stopSwipePipToHome(
mSwipePipToHomeAnimator.getComponentName(),
mSwipePipToHomeAnimator.getDestinationBounds(),
@ -1680,8 +1723,8 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
* depend on proper class initialization.
*/
protected void initAfterSubclassConstructor() {
initTransitionEndpoints(
mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile());
initTransitionEndpoints(mRemoteTargetHandles[0].mTaskViewSimulator
.getOrientationState().getLauncherDeviceProfile());
}
protected void performHapticFeedback() {
@ -1698,7 +1741,8 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
protected void linkRecentsViewScroll() {
SurfaceTransactionApplier.create(mRecentsView, applier -> {
mTransformParams.setSyncTransactionApplier(applier);
runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.mTransformParams
.setSyncTransactionApplier(applier));
runOnRecentsAnimationStart(() ->
mRecentsAnimationTargets.addReleaseCheck(applier));
});
@ -1824,19 +1868,25 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
/**
* Applies the transform on the recents animation
*/
protected void applyWindowTransform() {
if (mWindowTransitionController != null) {
mWindowTransitionController.setProgress(
Math.max(mCurrentShift.value, getScaleProgressDueToScroll()),
mDragLengthFactor);
}
protected void applyScrollAndTransform() {
// No need to apply any transform if there is ongoing swipe-pip-to-home animator since
// that animator handles the leash solely.
if (mRecentsAnimationTargets != null && !mIsSwipingPipToHome) {
if (mRecentsViewScrollLinked && mRecentsView != null) {
mTaskViewSimulator.setScroll(mRecentsView.getScrollOffset());
boolean notSwipingPipToHome = mRecentsAnimationTargets != null && !mIsSwipingPipToHome;
boolean setRecentsScroll = mRecentsViewScrollLinked && mRecentsView != null;
for (RemoteTargetHandle remoteHandle : mRemoteTargetHandles) {
AnimatorControllerWithResistance playbackController = remoteHandle.mPlaybackController;
if (playbackController != null) {
playbackController.setProgress(Math.max(mCurrentShift.value,
getScaleProgressDueToScroll()), mDragLengthFactor);
}
if (notSwipingPipToHome) {
TaskViewSimulator taskViewSimulator = remoteHandle.mTaskViewSimulator;
if (setRecentsScroll) {
taskViewSimulator.setScroll(mRecentsView.getScrollOffset());
}
taskViewSimulator.apply(remoteHandle.mTransformParams);
}
mTaskViewSimulator.apply(mTransformParams);
}
ProtoTracer.INSTANCE.get(mContext).scheduleFrameUpdate();
}
@ -1891,7 +1941,6 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
}
public interface Factory {
AbsSwipeUpHandler newHandler(GestureState gestureState, long touchTimeMs);
}
}

View File

@ -52,6 +52,7 @@ import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.WindowBounds;
import com.android.launcher3.views.ScrimView;
import com.android.quickstep.SysUINavigationMode.Mode;
@ -200,11 +201,37 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T
recentsView.switchToScreenshot(thumbnailData, runnable);
}
/**
* Sets the task size in {@param outRect} taking split screened windows into account.
* We assume combined height of both tasks will be same as one normal task, then we'll modify
* the task height/width based on the ratio of task screen space bounds from
* {@param splitInfo}
*
* @param desiredStageBounds whether task size for top/left or bottom/right needs to be computed
*/
public final void calculateStagedSplitTaskSize(Context context, DeviceProfile dp, Rect outRect,
SplitConfigurationOptions.StagedSplitBounds splitInfo,
@SplitConfigurationOptions.StagePosition int desiredStageBounds) {
calculateTaskSize(context, dp, outRect);
// TODO(b/181705607) Change for landscape vs portrait
float totalHeight = splitInfo.mLeftTopBounds.height()
+ splitInfo.mRightBottomBounds.height()
+ splitInfo.mDividerBounds.height() / 2f;
float topTaskPercent = splitInfo.mLeftTopBounds.height() / totalHeight;
if (desiredStageBounds == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT) {
float diff = outRect.height() * (1f - topTaskPercent);
outRect.bottom -= diff;
} else {
float diff = outRect.height() * topTaskPercent;
outRect.top += diff;
}
}
/**
* Calculates the taskView size for the provided device configuration.
*/
public final void calculateTaskSize(Context context, DeviceProfile dp, Rect outRect,
PagedOrientationHandler orientedState) {
public final void calculateTaskSize(Context context, DeviceProfile dp, Rect outRect) {
Resources res = context.getResources();
if (dp.overviewShowAsGrid) {
Rect gridRect = new Rect();

View File

@ -57,7 +57,7 @@ public final class FallbackActivityInterface extends
@Override
public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect,
PagedOrientationHandler orientationHandler) {
calculateTaskSize(context, dp, outRect, orientationHandler);
calculateTaskSize(context, dp, outRect);
if (dp.isVerticalBarLayout()
&& SysUINavigationMode.INSTANCE.get(context).getMode() != NO_BUTTON) {
return dp.isSeascape() ? outRect.left : (dp.widthPx - outRect.right);

View File

@ -28,6 +28,7 @@ import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACT
import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.content.ActivityNotFoundException;
import android.content.Context;
@ -101,7 +102,9 @@ public class FallbackSwipeHandler extends
mRunningOverHome = ActivityManagerWrapper.isHomeTask(mGestureState.getRunningTask());
if (mRunningOverHome) {
mTransformParams.setHomeBuilderProxy(this::updateHomeActivityTransformDuringSwipeUp);
runActionOnRemoteHandles(remoteTargetHandle ->
remoteTargetHandle.mTransformParams.setHomeBuilderProxy(
FallbackSwipeHandler.this::updateHomeActivityTransformDuringSwipeUp));
}
}
@ -109,7 +112,8 @@ public class FallbackSwipeHandler extends
protected void initTransitionEndpoints(DeviceProfile dp) {
super.initTransitionEndpoints(dp);
if (mRunningOverHome) {
mMaxLauncherScale = 1 / mTaskViewSimulator.getFullScreenScale();
// Full screen scale should be independent of remote target handle
mMaxLauncherScale = 1 / mRemoteTargetHandles[0].mTaskViewSimulator.getFullScreenScale();
}
}
@ -174,7 +178,8 @@ public class FallbackSwipeHandler extends
protected void notifyGestureAnimationStartToRecents() {
if (mRunningOverHome) {
if (SysUINavigationMode.getMode(mContext).hasGestures) {
mRecentsView.onGestureAnimationStartOnHome(mGestureState.getRunningTask());
mRecentsView.onGestureAnimationStartOnHome(
new ActivityManager.RunningTaskInfo[]{mGestureState.getRunningTask()});
}
} else {
super.notifyGestureAnimationStartToRecents();
@ -202,19 +207,24 @@ public class FallbackSwipeHandler extends
mHomeAlpha = new AnimatedFloat();
mHomeAlpha.value = Utilities.boundToRange(1 - mCurrentShift.value, 0, 1);
mVerticalShiftForScale.value = mCurrentShift.value;
mTransformParams.setHomeBuilderProxy(
this::updateHomeActivityTransformDuringHomeAnim);
runActionOnRemoteHandles(remoteTargetHandle ->
remoteTargetHandle.mTransformParams.setHomeBuilderProxy(
FallbackHomeAnimationFactory.this
::updateHomeActivityTransformDuringHomeAnim));
} else {
mHomeAlpha = new AnimatedFloat(this::updateHomeAlpha);
mHomeAlpha.value = 0;
mHomeAlphaParams.setHomeBuilderProxy(
this::updateHomeActivityTransformDuringHomeAnim);
runActionOnRemoteHandles(remoteTargetHandle ->
remoteTargetHandle.mTransformParams.setHomeBuilderProxy(
FallbackHomeAnimationFactory.this
::updateHomeActivityTransformDuringHomeAnim));
}
mRecentsAlpha.value = 1;
mTransformParams.setBaseBuilderProxy(
this::updateRecentsActivityTransformDuringHomeAnim);
runActionOnRemoteHandles(remoteTargetHandle ->
remoteTargetHandle.mTransformParams.setHomeBuilderProxy(
FallbackHomeAnimationFactory.this
::updateRecentsActivityTransformDuringHomeAnim));
}
@NonNull

View File

@ -71,7 +71,7 @@ public final class LauncherActivityInterface extends
@Override
public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect,
PagedOrientationHandler orientationHandler) {
calculateTaskSize(context, dp, outRect, orientationHandler);
calculateTaskSize(context, dp, outRect);
if (dp.isVerticalBarLayout() && SysUINavigationMode.getMode(context) != Mode.NO_BUTTON) {
return dp.isSeascape() ? outRect.left : (dp.widthPx - outRect.right);
} else {

View File

@ -110,7 +110,7 @@ public class LauncherSwipeHandlerV2 extends
mActivity.setHintUserWillBeActive();
}
if (!canUseWorkspaceView || appCanEnterPip) {
if (!canUseWorkspaceView || appCanEnterPip || mIsSwipeForStagedSplit) {
return new LauncherHomeAnimationFactory();
}
if (workspaceView instanceof LauncherAppWidgetHostView) {
@ -181,14 +181,16 @@ public class LauncherSwipeHandlerV2 extends
final float floatingWidgetAlpha = isTargetTranslucent ? 0 : 1;
RectF backgroundLocation = new RectF();
Rect crop = new Rect();
mTaskViewSimulator.getCurrentCropRect().roundOut(crop);
// We can assume there is only one remote target here because staged split never animates
// into the app icon, only into the homescreen
mRemoteTargetHandles[0].mTaskViewSimulator.getCurrentCropRect().roundOut(crop);
Size windowSize = new Size(crop.width(), crop.height());
int fallbackBackgroundColor =
FloatingWidgetView.getDefaultBackgroundColor(mContext, runningTaskTarget);
FloatingWidgetView floatingWidgetView = FloatingWidgetView.getFloatingWidgetView(mActivity,
hostView, backgroundLocation, windowSize,
mTaskViewSimulator.getCurrentCornerRadius(), isTargetTranslucent,
fallbackBackgroundColor);
mRemoteTargetHandles[0].mTaskViewSimulator.getCurrentCornerRadius(),
isTargetTranslucent, fallbackBackgroundColor);
return new FloatingViewHomeAnimationFactory(floatingWidgetView) {

View File

@ -16,6 +16,7 @@
package com.android.quickstep;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
import android.graphics.Rect;
import android.util.ArraySet;
@ -30,6 +31,7 @@ import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.Arrays;
import java.util.Set;
/**
@ -93,8 +95,16 @@ public class RecentsAnimationCallbacks implements
RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets,
Rect homeContentInsets, Rect minimizedHomeBounds) {
// Convert appTargets to type RemoteAnimationTarget for all apps except Home app
RemoteAnimationTarget[] nonHomeApps = Arrays.stream(appTargets)
.filter(remoteAnimationTarget ->
remoteAnimationTarget.activityType != ACTIVITY_TYPE_HOME)
.map(RemoteAnimationTargetCompat::unwrap)
.toArray(RemoteAnimationTarget[]::new);
RemoteAnimationTarget[] nonAppTargets =
mSystemUiProxy.onGoingToRecentsLegacy(mCancelled);
mSystemUiProxy.onGoingToRecentsLegacy(mCancelled, nonHomeApps);
RecentsAnimationTargets targets = new RecentsAnimationTargets(appTargets,
wallpaperTargets, RemoteAnimationTargetCompat.wrap(nonAppTargets),
homeContentInsets, minimizedHomeBounds);

View File

@ -77,8 +77,12 @@ public class RemoteAnimationTargets {
* Gets the navigation bar remote animation target if exists.
*/
public RemoteAnimationTargetCompat getNavBarRemoteAnimationTarget() {
return getNonAppTargetOfType(TYPE_NAVIGATION_BAR);
}
public RemoteAnimationTargetCompat getNonAppTargetOfType(int type) {
for (RemoteAnimationTargetCompat target : nonApps) {
if (target.windowType == TYPE_NAVIGATION_BAR) {
if (target.windowType == type) {
return target;
}
}

View File

@ -15,9 +15,13 @@
*/
package com.android.quickstep;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_SELECT;
import static com.android.launcher3.config.FeatureFlags.PROTOTYPE_APP_CLOSE;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import android.animation.Animator;
import android.content.Context;
@ -36,8 +40,11 @@ import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.AppCloseConfig;
import com.android.quickstep.util.LauncherSplitScreenListener;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.RectFSpringAnim2;
import com.android.quickstep.util.TaskViewSimulator;
@ -46,7 +53,11 @@ import com.android.quickstep.util.TransformParams.BuilderProxy;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
public abstract class SwipeUpAnimationLogic {
import java.util.Arrays;
import java.util.function.Consumer;
public abstract class SwipeUpAnimationLogic implements
RecentsAnimationCallbacks.RecentsAnimationListener{
protected static final Rect TEMP_RECT = new Rect();
@ -55,9 +66,9 @@ public abstract class SwipeUpAnimationLogic {
protected final Context mContext;
protected final RecentsAnimationDeviceState mDeviceState;
protected final GestureState mGestureState;
protected final TaskViewSimulator mTaskViewSimulator;
protected final TransformParams mTransformParams;
protected final RemoteTargetHandle[] mRemoteTargetHandles;
protected SplitConfigurationOptions.StagedSplitBounds mStagedSplitBounds;
// Shift in the range of [0, 1].
// 0 => preview snapShot is completely visible, and hotseat is completely translated down
@ -70,37 +81,56 @@ public abstract class SwipeUpAnimationLogic {
// How much further we can drag past recents, as a factor of mTransitionDragLength.
protected float mDragLengthFactor = 1;
protected AnimatorControllerWithResistance mWindowTransitionController;
protected boolean mIsSwipeForStagedSplit;
public SwipeUpAnimationLogic(Context context, RecentsAnimationDeviceState deviceState,
GestureState gestureState, TransformParams transformParams) {
mContext = context;
mDeviceState = deviceState;
mGestureState = gestureState;
mTaskViewSimulator = new TaskViewSimulator(context, gestureState.getActivityInterface());
mTransformParams = transformParams;
mTaskViewSimulator.getOrientationState().update(
mIsSwipeForStagedSplit = ENABLE_SPLIT_SELECT.get() &&
LauncherSplitScreenListener.INSTANCE.getNoCreate().getSplitTaskIds().length > 1;
TaskViewSimulator primaryTVS = new TaskViewSimulator(context,
gestureState.getActivityInterface());
primaryTVS.getOrientationState().update(
mDeviceState.getRotationTouchHelper().getCurrentActiveRotation(),
mDeviceState.getRotationTouchHelper().getDisplayRotation());
mRemoteTargetHandles = new RemoteTargetHandle[mIsSwipeForStagedSplit ? 2 : 1];
mRemoteTargetHandles[0] = new RemoteTargetHandle(primaryTVS, transformParams);
if (mIsSwipeForStagedSplit) {
TaskViewSimulator secondaryTVS = new TaskViewSimulator(context,
gestureState.getActivityInterface());
secondaryTVS.getOrientationState().update(
mDeviceState.getRotationTouchHelper().getCurrentActiveRotation(),
mDeviceState.getRotationTouchHelper().getDisplayRotation());
mRemoteTargetHandles[1] = new RemoteTargetHandle(secondaryTVS, new TransformParams());
}
}
protected void initTransitionEndpoints(DeviceProfile dp) {
mDp = dp;
mTaskViewSimulator.setDp(dp);
mTransitionDragLength = mGestureState.getActivityInterface().getSwipeUpDestinationAndLength(
dp, mContext, TEMP_RECT,
mTaskViewSimulator.getOrientationState().getOrientationHandler());
dp, mContext, TEMP_RECT, mRemoteTargetHandles[0].mTaskViewSimulator
.getOrientationState().getOrientationHandler());
mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength;
PendingAnimation pa = new PendingAnimation(mTransitionDragLength * 2);
mTaskViewSimulator.addAppToOverviewAnim(pa, LINEAR);
AnimatorPlaybackController normalController = pa.createPlaybackController();
mWindowTransitionController = AnimatorControllerWithResistance.createForRecents(
normalController, mContext, mTaskViewSimulator.getOrientationState(),
mDp, mTaskViewSimulator.recentsViewScale, AnimatedFloat.VALUE,
mTaskViewSimulator.recentsViewSecondaryTranslation, AnimatedFloat.VALUE);
for (RemoteTargetHandle remoteHandle : mRemoteTargetHandles) {
PendingAnimation pendingAnimation = new PendingAnimation(mTransitionDragLength * 2);
TaskViewSimulator taskViewSimulator = remoteHandle.mTaskViewSimulator;
taskViewSimulator.setDp(dp);
taskViewSimulator.addAppToOverviewAnim(pendingAnimation, LINEAR);
AnimatorPlaybackController playbackController =
pendingAnimation.createPlaybackController();
remoteHandle.mPlaybackController = AnimatorControllerWithResistance.createForRecents(
playbackController, mContext, taskViewSimulator.getOrientationState(),
mDp, taskViewSimulator.recentsViewScale, AnimatedFloat.VALUE,
taskViewSimulator.recentsViewSecondaryTranslation, AnimatedFloat.VALUE
);
}
}
@UiThread
@ -125,7 +155,9 @@ public abstract class SwipeUpAnimationLogic {
public abstract void updateFinalShift();
protected PagedOrientationHandler getOrientationHandler() {
return mTaskViewSimulator.getOrientationState().getOrientationHandler();
// OrientationHandler should be independent of remote target, can directly take one
return mRemoteTargetHandles[0].mTaskViewSimulator
.getOrientationState().getOrientationHandler();
}
protected abstract class HomeAnimationFactory {
@ -207,31 +239,102 @@ public abstract class SwipeUpAnimationLogic {
* @param startProgress The progress of {@link #mCurrentShift} to start thw window from.
* @return {@link RectF} represents the bounds as starting point in window space.
*/
protected RectF updateProgressForStartRect(Matrix outMatrix, float startProgress) {
protected RectF[] updateProgressForStartRect(Matrix outMatrix, float startProgress) {
mCurrentShift.updateValue(startProgress);
mTaskViewSimulator.apply(mTransformParams.setProgress(startProgress));
RectF cropRectF = new RectF(mTaskViewSimulator.getCurrentCropRect());
RectF[] startRects = new RectF[mRemoteTargetHandles.length];
for (int i = 0, mRemoteTargetHandlesLength = mRemoteTargetHandles.length;
i < mRemoteTargetHandlesLength; i++) {
RemoteTargetHandle remoteHandle = mRemoteTargetHandles[i];
TaskViewSimulator tvs = remoteHandle.mTaskViewSimulator;
tvs.apply(remoteHandle.mTransformParams.setProgress(startProgress));
mTaskViewSimulator.applyWindowToHomeRotation(outMatrix);
final RectF startRect = new RectF(cropRectF);
mTaskViewSimulator.getCurrentMatrix().mapRect(startRect);
return startRect;
startRects[i] = new RectF(tvs.getCurrentCropRect());
tvs.applyWindowToHomeRotation(outMatrix);
tvs.getCurrentMatrix().mapRect(startRects[i]);
}
return startRects;
}
/** Helper to avoid writing some for-loops to iterate over {@link #mRemoteTargetHandles} */
protected void runActionOnRemoteHandles(Consumer<RemoteTargetHandle> consumer) {
for (RemoteTargetHandle handle : mRemoteTargetHandles) {
consumer.accept(handle);
}
}
/** @return only the TaskViewSimulators from {@link #mRemoteTargetHandles} */
protected TaskViewSimulator[] getRemoteTaskViewSimulators() {
return Arrays.stream(mRemoteTargetHandles)
.map(remoteTargetHandle -> remoteTargetHandle.mTaskViewSimulator)
.toArray(TaskViewSimulator[]::new);
}
@Override
public void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targets) {
ActiveGestureLog.INSTANCE.addLog("startRecentsAnimationCallback", targets.apps.length);
RemoteAnimationTargetCompat dividerTarget = targets.getNonAppTargetOfType(
TYPE_DOCK_DIVIDER);
RemoteAnimationTargetCompat primaryTaskTarget;
RemoteAnimationTargetCompat secondaryTaskTarget;
if (!mIsSwipeForStagedSplit) {
primaryTaskTarget = targets.findTask(mGestureState.getRunningTaskId());
mRemoteTargetHandles[0].mTransformParams.setTargetSet(targets);
if (primaryTaskTarget != null) {
mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(primaryTaskTarget);
}
} else {
// We're in staged split
primaryTaskTarget = targets.apps[0];
secondaryTaskTarget = targets.apps[1];
mStagedSplitBounds = new SplitConfigurationOptions.StagedSplitBounds(
primaryTaskTarget.screenSpaceBounds,
secondaryTaskTarget.screenSpaceBounds, dividerTarget.screenSpaceBounds);
mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(primaryTaskTarget,
mStagedSplitBounds);
mRemoteTargetHandles[1].mTaskViewSimulator.setPreview(secondaryTaskTarget,
mStagedSplitBounds);
mRemoteTargetHandles[0].mTransformParams.setTargetSet(
createRemoteAnimationTargetsForTarget(primaryTaskTarget));
mRemoteTargetHandles[1].mTransformParams.setTargetSet(
createRemoteAnimationTargetsForTarget(secondaryTaskTarget));
}
}
private RemoteAnimationTargets createRemoteAnimationTargetsForTarget(
RemoteAnimationTargetCompat target) {
return new RemoteAnimationTargets(new RemoteAnimationTargetCompat[]{target},
null, null, MODE_CLOSING);
}
/**
* Creates an animation that transforms the current app window into the home app.
* @param startProgress The progress of {@link #mCurrentShift} to start the window from.
* @param homeAnimationFactory The home animation factory.
*/
protected RectFSpringAnim createWindowAnimationToHome(float startProgress,
protected RectFSpringAnim[] createWindowAnimationToHome(float startProgress,
HomeAnimationFactory homeAnimationFactory) {
// TODO(b/195473584) compute separate end targets for different staged split
final RectF targetRect = homeAnimationFactory.getWindowTargetRect();
RectFSpringAnim[] out = new RectFSpringAnim[mRemoteTargetHandles.length];
Matrix homeToWindowPositionMap = new Matrix();
final RectF startRect = updateProgressForStartRect(homeToWindowPositionMap, startProgress);
RectF cropRectF = new RectF(mTaskViewSimulator.getCurrentCropRect());
RectF[] startRects = updateProgressForStartRect(homeToWindowPositionMap, startProgress);
for (int i = 0, mRemoteTargetHandlesLength = mRemoteTargetHandles.length;
i < mRemoteTargetHandlesLength; i++) {
RemoteTargetHandle remoteHandle = mRemoteTargetHandles[i];
out[i] = getWindowAnimationToHomeInternal(homeAnimationFactory,
targetRect, remoteHandle.mTransformParams, remoteHandle.mTaskViewSimulator,
startRects[i], homeToWindowPositionMap);
}
return out;
}
private RectFSpringAnim getWindowAnimationToHomeInternal(
HomeAnimationFactory homeAnimationFactory, RectF targetRect,
TransformParams transformParams, TaskViewSimulator taskViewSimulator,
RectF startRect, Matrix homeToWindowPositionMap) {
RectF cropRectF = new RectF(taskViewSimulator.getCurrentCropRect());
// Move the startRect to Launcher space as floatingIconView runs in Launcher
Matrix windowToHomePositionMap = new Matrix();
homeToWindowPositionMap.invert(windowToHomePositionMap);
@ -240,7 +343,7 @@ public abstract class SwipeUpAnimationLogic {
RectFSpringAnim anim;
if (PROTOTYPE_APP_CLOSE.get()) {
anim = new RectFSpringAnim2(startRect, targetRect, mContext,
mTaskViewSimulator.getCurrentCornerRadius(),
taskViewSimulator.getCurrentCornerRadius(),
homeAnimationFactory.getEndRadius(cropRectF));
} else {
anim = new RectFSpringAnim(startRect, targetRect, mContext);
@ -248,9 +351,10 @@ public abstract class SwipeUpAnimationLogic {
homeAnimationFactory.setAnimation(anim);
SpringAnimationRunner runner = new SpringAnimationRunner(
homeAnimationFactory, cropRectF, homeToWindowPositionMap);
anim.addOnUpdateListener(runner);
homeAnimationFactory, cropRectF, homeToWindowPositionMap,
transformParams, taskViewSimulator);
anim.addAnimatorListener(runner);
anim.addOnUpdateListener(runner);
return anim;
}
@ -262,6 +366,7 @@ public abstract class SwipeUpAnimationLogic {
final RectF mWindowCurrentRect = new RectF();
final Matrix mHomeToWindowPositionMap;
private final TransformParams mLocalTransformParams;
final HomeAnimationFactory mAnimationFactory;
final AnimatorPlaybackController mHomeAnim;
@ -271,17 +376,19 @@ public abstract class SwipeUpAnimationLogic {
final float mEndRadius;
SpringAnimationRunner(HomeAnimationFactory factory, RectF cropRectF,
Matrix homeToWindowPositionMap) {
Matrix homeToWindowPositionMap, TransformParams transformParams,
TaskViewSimulator taskViewSimulator) {
mAnimationFactory = factory;
mHomeAnim = factory.createActivityAnimationToHome();
mCropRectF = cropRectF;
mHomeToWindowPositionMap = homeToWindowPositionMap;
mLocalTransformParams = transformParams;
cropRectF.roundOut(mCropRect);
// End on a "round-enough" radius so that the shape reveal doesn't have to do too much
// rounding at the end of the animation.
mStartRadius = mTaskViewSimulator.getCurrentCornerRadius();
mStartRadius = taskViewSimulator.getCurrentCornerRadius();
mEndRadius = factory.getEndRadius(cropRectF);
}
@ -300,10 +407,11 @@ public abstract class SwipeUpAnimationLogic {
if (mAnimationFactory.keepWindowOpaque()) {
alpha = 1f;
}
mTransformParams
mLocalTransformParams
.setTargetAlpha(alpha)
.setCornerRadius(cornerRadius);
mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this));
mLocalTransformParams.applySurfaceParams(mLocalTransformParams
.createSurfaceParams(this));
mAnimationFactory.update(config, currentRect, progress,
mMatrix.mapRadius(cornerRadius));
}
@ -332,6 +440,21 @@ public abstract class SwipeUpAnimationLogic {
}
}
/**
* Container to keep together all the associated objects whose properties need to be updated to
* animate a single remote app target
*/
public static class RemoteTargetHandle {
public TaskViewSimulator mTaskViewSimulator;
public TransformParams mTransformParams;
public AnimatorControllerWithResistance mPlaybackController;
public RemoteTargetHandle(TaskViewSimulator taskViewSimulator,
TransformParams transformParams) {
mTransformParams = transformParams;
mTaskViewSimulator = taskViewSimulator;
}
}
public interface RunningWindowAnim {
void end();

View File

@ -17,6 +17,7 @@ package com.android.quickstep;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.app.ActivityManager;
import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.content.ComponentName;
@ -632,10 +633,11 @@ public class SystemUiProxy implements ISystemUiProxy,
* @param cancel true if recents starting is being cancelled.
* @return RemoteAnimationTargets of windows that need to animate but only exist in shell.
*/
public RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel) {
public RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel,
RemoteAnimationTarget[] apps) {
if (mSplitScreen != null) {
try {
return mSplitScreen.onGoingToRecentsLegacy(cancel);
return mSplitScreen.onGoingToRecentsLegacy(cancel, apps);
} catch (RemoteException e) {
Log.w(TAG, "Failed call onGoingToRecentsLegacy");
}

View File

@ -154,9 +154,10 @@ public final class TaskViewUtils {
boolean isRunningTask = v.isRunningTask();
TransformParams params = null;
TaskViewSimulator tsv = null;
// TODO(b/195675206) handle two TSVs here
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask) {
params = v.getRecentsView().getLiveTileParams();
tsv = v.getRecentsView().getLiveTileTaskViewSimulator();
params = v.getRecentsView().getRemoteTargetHandles()[0].mTransformParams;
tsv = v.getRecentsView().getRemoteTargetHandles()[0].mTaskViewSimulator;
}
createRecentsWindowAnimator(v, skipViewChanges, appTargets, wallpaperTargets, nonAppTargets,
depthController, out, params, tsv);

View File

@ -99,6 +99,7 @@ import com.android.quickstep.inputconsumers.SysUiOverlayInputConsumer;
import com.android.quickstep.inputconsumers.TaskbarStashInputConsumer;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.AssistantUtilities;
import com.android.quickstep.util.LauncherSplitScreenListener;
import com.android.quickstep.util.ProtoTracer;
import com.android.quickstep.util.ProxyScreenStatusProvider;
import com.android.quickstep.util.SplitScreenBounds;
@ -364,6 +365,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
mDeviceState.runOnUserUnlocked(this::onUserUnlocked);
mDeviceState.runOnUserUnlocked(mTaskbarManager::onUserUnlocked);
ProtoTracer.INSTANCE.get(this).add(this);
LauncherSplitScreenListener.INSTANCE.get(this).init();
sConnected = true;
}
@ -520,6 +522,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
getSystemService(AccessibilityManager.class)
.unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS);
LauncherSplitScreenListener.INSTANCE.get(this).destroy();
mTaskbarManager.destroy();
sConnected = false;
super.onDestroy();

View File

@ -79,8 +79,10 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsSta
* to the home task. This allows us to handle quick-switch similarly to a quick-switching
* from a foreground task.
*/
public void onGestureAnimationStartOnHome(RunningTaskInfo homeTaskInfo) {
mHomeTaskInfo = homeTaskInfo;
public void onGestureAnimationStartOnHome(RunningTaskInfo[] homeTaskInfo) {
// TODO(b/195607777) General fallback love, but this might be correct
// Home task should be defined as the front-most task info I think?
mHomeTaskInfo = homeTaskInfo[0];
onGestureAnimationStart(homeTaskInfo);
}
@ -92,8 +94,8 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsSta
@Override
public void onPrepareGestureEndAnimation(
@Nullable AnimatorSet animatorSet, GestureState.GestureEndTarget endTarget,
TaskViewSimulator taskViewSimulator) {
super.onPrepareGestureEndAnimation(animatorSet, endTarget, taskViewSimulator);
TaskViewSimulator[] taskViewSimulators) {
super.onPrepareGestureEndAnimation(animatorSet, endTarget, taskViewSimulators);
if (mHomeTaskInfo != null && endTarget == RECENTS && animatorSet != null) {
TaskView tv = getTaskViewByTaskId(mHomeTaskInfo.taskId);
if (tv != null) {
@ -133,7 +135,13 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsSta
}
@Override
protected boolean shouldAddStubTaskView(RunningTaskInfo runningTaskInfo) {
protected boolean shouldAddStubTaskView(RunningTaskInfo[] runningTaskInfos) {
if (runningTaskInfos.length > 1) {
// can't be in split screen w/ home task
return super.shouldAddStubTaskView(runningTaskInfos);
}
RunningTaskInfo runningTaskInfo = runningTaskInfos[0];
if (mHomeTaskInfo != null && runningTaskInfo != null &&
mHomeTaskInfo.taskId == runningTaskInfo.taskId
&& getTaskViewCount() == 0) {
@ -141,7 +149,7 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsSta
// show the empty recents message instead of showing a stub task and later removing it.
return false;
}
return super.shouldAddStubTaskView(runningTaskInfo);
return super.shouldAddStubTaskView(runningTaskInfos);
}
@Override
@ -149,6 +157,7 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsSta
// When quick-switching on 3p-launcher, we add a "stub" tile corresponding to Launcher
// as well. This tile is never shown as we have setCurrentTaskHidden, but allows use to
// track the index of the next task appropriately, as if we are switching on any other app.
// TODO(b/195607777) Confirm home task info is front-most task and not mixed in with others
int runningTaskId = getTaskIdsForRunningTaskView()[0];
if (mHomeTaskInfo != null && mHomeTaskInfo.taskId == runningTaskId && !tasks.isEmpty()) {
// Check if the task list has running task

View File

@ -46,6 +46,7 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.GestureState;
import com.android.quickstep.OverviewComponentObserver;
@ -263,14 +264,16 @@ abstract class SwipeUpGestureTutorialController extends TutorialController {
void initDp(DeviceProfile dp) {
initTransitionEndpoints(dp);
mTaskViewSimulator.setPreviewBounds(
mRemoteTargetHandles[0].mTaskViewSimulator.setPreviewBounds(
new Rect(0, 0, dp.widthPx, dp.heightPx), dp.getInsets());
}
@Override
public void updateFinalShift() {
mWindowTransitionController.setProgress(mCurrentShift.value, mDragLengthFactor);
mTaskViewSimulator.apply(mTransformParams);
mRemoteTargetHandles[0].mPlaybackController
.setProgress(mCurrentShift.value, mDragLengthFactor);
mRemoteTargetHandles[0].mTaskViewSimulator.apply(
mRemoteTargetHandles[0].mTransformParams);
}
AnimatedFloat getCurrentShift() {
@ -326,7 +329,8 @@ abstract class SwipeUpGestureTutorialController extends TutorialController {
mFakeIconView.setVisibility(View.INVISIBLE);
}
};
RectFSpringAnim windowAnim = createWindowAnimationToHome(startShift, homeAnimFactory);
RectFSpringAnim windowAnim = createWindowAnimationToHome(startShift,
homeAnimFactory)[0];
windowAnim.start(mContext, velocityPxPerMs);
return windowAnim;
}

View File

@ -158,8 +158,7 @@ public class AnimatorControllerWithResistance {
Rect startRect = new Rect();
PagedOrientationHandler orientationHandler = params.recentsOrientedState
.getOrientationHandler();
LauncherActivityInterface.INSTANCE.calculateTaskSize(params.context, params.dp, startRect,
orientationHandler);
LauncherActivityInterface.INSTANCE.calculateTaskSize(params.context, params.dp, startRect);
long distanceToCover = startRect.bottom;
PendingAnimation resistAnim = params.resistAnim != null
? params.resistAnim

View File

@ -0,0 +1,104 @@
package com.android.quickstep.util;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
import android.content.Context;
import android.os.IBinder;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
import com.android.launcher3.util.SplitConfigurationOptions.StageType;
import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitTaskPosition;
import com.android.quickstep.SystemUiProxy;
import com.android.wm.shell.splitscreen.ISplitScreenListener;
/**
* Listeners for system wide split screen position and stage changes.
* Use {@link #getSplitTaskIds()} to determine which tasks, if any, are in staged split.
*/
public class LauncherSplitScreenListener extends ISplitScreenListener.Stub {
public static final MainThreadInitializedObject<LauncherSplitScreenListener> INSTANCE =
new MainThreadInitializedObject<>(LauncherSplitScreenListener::new);
private final StagedSplitTaskPosition mMainStagePosition = new StagedSplitTaskPosition();
private final StagedSplitTaskPosition mSideStagePosition = new StagedSplitTaskPosition();
public LauncherSplitScreenListener(Context context) {
mMainStagePosition.stageType = SplitConfigurationOptions.STAGE_TYPE_MAIN;
mSideStagePosition.stageType = SplitConfigurationOptions.STAGE_TYPE_SIDE;
}
/** Also call {@link #destroy()} when done. */
public void init() {
SystemUiProxy.INSTANCE.getNoCreate().registerSplitScreenListener(this);
}
public void destroy() {
SystemUiProxy.INSTANCE.getNoCreate().unregisterSplitScreenListener(this);
}
/**
* @return index 0 will be task in left/top position, index 1 in right/bottom position.
* Will return empty array if device is not in staged split
*/
public int[] getSplitTaskIds() {
if (mMainStagePosition.taskId == -1 || mSideStagePosition.taskId == -1) {
return new int[]{};
}
int[] out = new int[2];
if (mMainStagePosition.stagePosition == STAGE_POSITION_TOP_OR_LEFT) {
out[0] = mMainStagePosition.taskId;
out[1] = mSideStagePosition.taskId;
} else {
out[1] = mMainStagePosition.taskId;
out[0] = mSideStagePosition.taskId;
}
return out;
}
@Override
public void onStagePositionChanged(@StageType int stage, @StagePosition int position) {
if (stage == SplitConfigurationOptions.STAGE_TYPE_MAIN) {
mMainStagePosition.stagePosition = position;
} else {
mSideStagePosition.stagePosition = position;
}
}
@Override
public void onTaskStageChanged(int taskId, @StageType int stage, boolean visible) {
// If task is not visible but we are tracking it, stop tracking it
if (!visible) {
if (mMainStagePosition.taskId == taskId) {
resetTaskId(mMainStagePosition);
} else if (mSideStagePosition.taskId == taskId) {
resetTaskId(mSideStagePosition);
} // else it's an un-tracked child
return;
}
// If stage has moved to undefined, stop tracking the task
if (stage == SplitConfigurationOptions.STAGE_TYPE_UNDEFINED) {
resetTaskId(taskId == mMainStagePosition.taskId ?
mMainStagePosition : mSideStagePosition);
return;
}
if (stage == SplitConfigurationOptions.STAGE_TYPE_MAIN) {
mMainStagePosition.taskId = taskId;
} else {
mSideStagePosition.taskId = taskId;
}
}
private void resetTaskId(StagedSplitTaskPosition taskPosition) {
taskPosition.taskId = -1;
}
@Override
public IBinder asBinder() {
return this;
}
}

View File

@ -42,8 +42,7 @@ public class LayoutUtils {
PagedOrientationHandler orientationHandler) {
// Track the bottom of the window.
Rect taskSize = new Rect();
LauncherActivityInterface.INSTANCE.calculateTaskSize(
context, dp, taskSize, orientationHandler);
LauncherActivityInterface.INSTANCE.calculateTaskSize(context, dp, taskSize);
return orientationHandler.getDistanceToBottomOfRect(dp, taskSize);
}

View File

@ -17,6 +17,7 @@ package com.android.quickstep.util;
import static com.android.launcher3.states.RotationHelper.deltaRotation;
import static com.android.launcher3.touch.PagedOrientationHandler.MATRIX_POST_TRANSLATE;
import static com.android.launcher3.util.SplitConfigurationOptions.*;
import static com.android.quickstep.util.RecentsOrientedState.postDisplayRotation;
import static com.android.quickstep.util.RecentsOrientedState.preDisplayRotation;
import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN;
@ -29,6 +30,7 @@ import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.Log;
import androidx.annotation.NonNull;
@ -49,8 +51,13 @@ import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.
*/
public class TaskViewSimulator implements TransformParams.BuilderProxy {
private final String TAG = "TaskViewSimulator";
private final boolean DEBUG = false;
private final Rect mTmpCropRect = new Rect();
private final RectF mTempRectF = new RectF();
// Additional offset for split tasks
private final Point mSplitOffset = new Point();
private final float[] mTempPoint = new float[2];
private final Context mContext;
@ -63,6 +70,8 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
private final Rect mTaskRect = new Rect();
private final PointF mPivot = new PointF();
private DeviceProfile mDp;
@StagePosition
private int mStagePosition = STAGE_POSITION_UNDEFINED;
private final Matrix mMatrix = new Matrix();
private final Matrix mMatrixTmp = new Matrix();
@ -89,6 +98,7 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
// Cached calculations
private boolean mLayoutValid = false;
private int mOrientationStateId;
private StagedSplitBounds mStagedSplitBounds;
public TaskViewSimulator(Context context, BaseActivityInterface sizeStrategy) {
mContext = context;
@ -128,9 +138,19 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
if (mDp == null) {
return 1;
}
mSizeStrategy.calculateTaskSize(mContext, mDp, mTaskRect,
mOrientationState.getOrientationHandler());
return mOrientationState.getFullScreenScaleAndPivot(mTaskRect, mDp, mPivot);
Rect fullTaskSize = new Rect();
mSizeStrategy.calculateTaskSize(mContext, mDp, fullTaskSize);
if (mStagedSplitBounds != null) {
// The task rect changes according to the staged split task sizes, but recents
// fullscreen scale and pivot remains the same since the task fits into the existing
// sized task space bounds
mSizeStrategy.calculateStagedSplitTaskSize(mContext, mDp, mTaskRect, mStagedSplitBounds,
mStagePosition);
} else {
mTaskRect.set(fullTaskSize);
}
return mOrientationState.getFullScreenScaleAndPivot(fullTaskSize, mDp, mPivot);
}
/**
@ -142,6 +162,24 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
runningTarget.screenSpaceBounds.top);
}
/**
* Sets the targets which the simulator will control specifically for targets to animate when
* in split screen
*
* @param splitInfo set to {@code null} when not in staged split mode
*/
public void setPreview(RemoteAnimationTargetCompat runningTarget, StagedSplitBounds splitInfo) {
setPreview(runningTarget);
mStagedSplitBounds = splitInfo;
if (mStagedSplitBounds == null) {
mStagePosition = STAGE_POSITION_UNDEFINED;
return;
}
mStagePosition = mThumbnailPosition.equals(splitInfo.mLeftTopBounds) ?
STAGE_POSITION_TOP_OR_LEFT :
STAGE_POSITION_BOTTOM_OR_RIGHT;
}
/**
* Sets the targets which the simulator will control
*/
@ -239,6 +277,15 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
getFullScreenScale();
mThumbnailData.rotation = mOrientationState.getDisplayRotation();
// TODO(b/195145340) handle non 50-50 split scenarios
if (mStagedSplitBounds != null) {
if (mStagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
// The preview set is for the bottom/right, inset by top/left task
mSplitOffset.y = mStagedSplitBounds.mLeftTopBounds.height() +
mStagedSplitBounds.mDividerBounds.height() / 2;
}
}
// mIsRecentsRtl is the inverse of TaskView RTL.
boolean isRtlEnabled = !mIsRecentsRtl;
mPositionHelper.updateThumbnailMatrix(
@ -246,6 +293,9 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
mTaskRect.width(), mTaskRect.height(),
mDp, mOrientationState.getRecentsActivityRotation(), isRtlEnabled);
mPositionHelper.getMatrix().invert(mInversePositionMatrix);
if (DEBUG) {
Log.d(TAG, " taskRect: " + mTaskRect + " splitOffset: " + mSplitOffset);
}
}
float fullScreenProgress = Utilities.boundToRange(this.fullScreenProgress.value, 0, 1);
@ -280,6 +330,9 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
recentsViewPrimaryTranslation.value);
applyWindowToHomeRotation(mMatrix);
// Move lower/right split window into correct position
mMatrix.postTranslate(0, mSplitOffset.y);
// Crop rect is the inverse of thumbnail matrix
mTempRectF.set(-insets.left, -insets.top,
taskWidth + insets.right, taskHeight + insets.bottom);
@ -287,6 +340,25 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
mTempRectF.roundOut(mTmpCropRect);
params.applySurfaceParams(params.createSurfaceParams(this));
if (!DEBUG) {
return;
}
Log.d(TAG, "progress: " + fullScreenProgress
+ " scale: " + scale
+ " recentsViewScale: " + recentsViewScale.value
+ " crop: " + mTmpCropRect
+ " radius: " + getCurrentCornerRadius()
+ " translate: " + mSplitOffset
+ " taskW: " + taskWidth + " H: " + taskHeight
+ " taskRect: " + mTaskRect
+ " taskPrimaryT: " + taskPrimaryTranslation.value
+ " recentsPrimaryT: " + recentsViewPrimaryTranslation.value
+ " recentsSecondaryT: " + recentsViewSecondaryTranslation.value
+ " taskSecondaryT: " + taskSecondaryTranslation.value
+ " recentsScroll: " + recentsViewScroll.value
+ " pivot: " + mPivot
);
}
@Override

View File

@ -0,0 +1,137 @@
package com.android.quickstep.views;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import android.content.Context;
import android.util.AttributeSet;
import android.view.ViewGroup;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.quickstep.RecentsModel;
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;
/**
* TaskView that contains and shows thumbnails for not one, BUT TWO(!!) tasks
*
* That's right. If you call within the next 5 minutes we'll go ahead and double your order and
* send you !! TWO !! Tasks along with their TaskThumbnailViews complimentary. On. The. House.
* And not only that, we'll even clean up your thumbnail request if you don't like it.
* All the benefits of one TaskView, except DOUBLED!
*
* (Icon loading sold separately, fees may apply. Shipping & Handling for Overlays not included).
*/
public class GroupedTaskView extends TaskView {
private Task mSecondaryTask;
private TaskThumbnailView mSnapshotView2;
private CancellableTask mThumbnailLoadRequest2;
public GroupedTaskView(Context context) {
super(context);
}
public GroupedTaskView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public GroupedTaskView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mSnapshotView2 = findViewById(R.id.bottomright_snapshot);
}
public void bind(Task primary, Task secondary, RecentsOrientedState orientedState) {
super.bind(primary, orientedState);
mSecondaryTask = secondary;
mTaskIdContainer[1] = secondary.key.id;
mTaskIdAttributeContainer[1] = new TaskIdAttributeContainer(secondary, mSnapshotView2);
mSnapshotView2.bind(secondary);
adjustThumbnailBoundsForSplit();
}
@Override
public void onTaskListVisibilityChanged(boolean visible, int changes) {
super.onTaskListVisibilityChanged(visible, changes);
if (visible) {
RecentsModel model = RecentsModel.INSTANCE.get(getContext());
TaskThumbnailCache thumbnailCache = model.getThumbnailCache();
if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
mThumbnailLoadRequest2 = thumbnailCache.updateThumbnailInBackground(mSecondaryTask,
thumbnailData -> mSnapshotView2.setThumbnail(
mSecondaryTask, thumbnailData
));
}
if (needsUpdate(changes, FLAG_UPDATE_ICON)) {
// TODO What's the Icon for this going to look like? :o
}
} else {
if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
mSnapshotView2.setThumbnail(null, null);
// Reset the task thumbnail reference as well (it will be fetched from the cache or
// reloaded next time we need it)
mSecondaryTask.thumbnail = null;
}
if (needsUpdate(changes, FLAG_UPDATE_ICON)) {
// TODO
}
}
}
@Override
protected void cancelPendingLoadTasks() {
super.cancelPendingLoadTasks();
if (mThumbnailLoadRequest2 != null) {
mThumbnailLoadRequest2.cancel();
mThumbnailLoadRequest2 = null;
}
}
@Override
public void onRecycle() {
super.onRecycle();
mSnapshotView2.setThumbnail(mSecondaryTask, null);
}
@Override
public void setOverlayEnabled(boolean overlayEnabled) {
super.setOverlayEnabled(overlayEnabled);
mSnapshotView2.setOverlayEnabled(overlayEnabled);
}
private void adjustThumbnailBoundsForSplit() {
DeviceProfile deviceProfile = mActivity.getDeviceProfile();
ViewGroup.LayoutParams primaryLp = mSnapshotView.getLayoutParams();
primaryLp.width = mSecondaryTask == null ?
MATCH_PARENT :
getWidth();
int spaceAboveSnapshot = deviceProfile.overviewTaskThumbnailTopMarginPx;
// TODO get divider height
int dividerBar = 20;
primaryLp.height = mSecondaryTask == null ?
MATCH_PARENT :
(getHeight() - spaceAboveSnapshot - dividerBar) / 2;
mSnapshotView.setLayoutParams(primaryLp);
if (mSecondaryTask == null) {
mSnapshotView2.setVisibility(GONE);
return;
}
mSnapshotView2.setVisibility(VISIBLE);
ViewGroup.LayoutParams secondaryLp = mSnapshotView2.getLayoutParams();
secondaryLp.width = getWidth();
secondaryLp.height = primaryLp.height;
mSnapshotView2.setLayoutParams(secondaryLp);
mSnapshotView2.setTranslationY(primaryLp.height + spaceAboveSnapshot + dividerBar);
}
}

View File

@ -19,6 +19,7 @@ package com.android.quickstep.views;
import static android.view.Surface.ROTATION_0;
import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.makeMeasureSpec;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU;
import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType;
@ -51,6 +52,7 @@ import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NON_ZERO_ROTATION;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NO_RECENTS;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NO_TASKS;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@ -143,11 +145,13 @@ import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.RecentsModel.TaskVisualsChangeListener;
import com.android.quickstep.RemoteAnimationTargets;
import com.android.quickstep.SwipeUpAnimationLogic.RemoteTargetHandle;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskOverlayFactory;
import com.android.quickstep.TaskThumbnailCache;
import com.android.quickstep.TaskViewUtils;
import com.android.quickstep.ViewUtils;
import com.android.quickstep.util.LauncherSplitScreenListener;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.util.SplitScreenBounds;
@ -317,7 +321,13 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
view.setScaleY(scale);
view.mLastComputedTaskStartPushOutDistance = null;
view.mLastComputedTaskEndPushOutDistance = null;
view.mLiveTileTaskViewSimulator.recentsViewScale.value = scale;
view.runActionOnRemoteHandles(new Consumer<RemoteTargetHandle>() {
@Override
public void accept(RemoteTargetHandle remoteTargetHandle) {
remoteTargetHandle.mTaskViewSimulator.recentsViewScale.value =
scale;
}
});
view.setTaskViewsResistanceTranslation(view.mTaskViewsSecondaryTranslation);
view.updatePageOffsets();
}
@ -365,8 +375,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
// mTaskGridVerticalDiff and mTopBottomRowHeightDiff summed together provides the top
// position for bottom row of grid tasks.
protected final TransformParams mLiveTileParams = new TransformParams();
protected final TaskViewSimulator mLiveTileTaskViewSimulator;
protected RemoteTargetHandle[] mRemoteTargetHandles;
protected final Rect mLastComputedTaskSize = new Rect();
protected final Rect mLastComputedGridSize = new Rect();
protected final Rect mLastComputedGridTaskSize = new Rect();
@ -402,9 +411,10 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
private final InvariantDeviceProfile mIdp;
/**
* Getting views should be done via {@link #getTaskViewFromPool()}
* Getting views should be done via {@link #getTaskViewFromPool(boolean)}
*/
private final ViewPool<TaskView> mTaskViewPool;
private final ViewPool<GroupedTaskView> mGroupedTaskViewPool;
private final TaskOverlayFactory mTaskOverlayFactory;
@ -504,13 +514,13 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
// Only valid until the launcher state changes to NORMAL
/**
* ID for the current running TaskView view, unique amongst TaskView instances. ID's are set
* through {@link #getTaskViewFromPool()} and incremented by {@link #mTaskViewIdCount}
* through {@link #getTaskViewFromPool(boolean)} and incremented by {@link #mTaskViewIdCount}
*/
protected int mRunningTaskViewId = -1;
private int mTaskViewIdCount;
private final int[] INVALID_TASK_IDS = new int[]{-1, -1};
protected boolean mRunningTaskTileHidden;
private Task mTmpRunningTask;
private Task[] mTmpRunningTasks;
protected int mFocusedTaskViewId = -1;
private boolean mTaskIconScaledDown = false;
@ -626,6 +636,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
mClearAllButton.setOnClickListener(this::dismissAllTasks);
mTaskViewPool = new ViewPool<>(context, this, R.layout.task, 20 /* max size */,
10 /* initial size */);
// There's only one pair of grouped tasks we can envision at the moment
mGroupedTaskViewPool = new ViewPool<>(context, this,
R.layout.task_grouped, 2 /* max size */, 1 /* initial size */);
mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources());
setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
@ -656,10 +669,6 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
// Initialize quickstep specific cache params here, as this is constructed only once
mActivity.getViewCache().setCacheSize(R.layout.digital_wellbeing_toast, 5);
mLiveTileTaskViewSimulator = new TaskViewSimulator(getContext(), getSizeStrategy());
mLiveTileTaskViewSimulator.recentsViewScale.value = 1;
mLiveTileTaskViewSimulator.setOrientationState(mOrientationState);
mTintingColor = getForegroundScrimDimColor(context);
}
@ -707,7 +716,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
super.dispatchDraw(canvas);
}
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile
&& mLiveTileParams.getTargetSet() != null) {
&& mRemoteTargetHandles != null) {
redrawLiveTile();
}
}
@ -749,9 +758,13 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
if (mHandleTaskStackChanges) {
TaskView taskView = getTaskViewByTaskId(taskId);
if (taskView != null) {
Task task = taskView.getTask();
taskView.getThumbnail().setThumbnail(task, thumbnailData);
return task;
for (TaskView.TaskIdAttributeContainer container :
taskView.getTaskIdAttributeContainers()) {
if (container == null || taskId != container.getTask().key.id) {
continue;
}
container.getThumbnailView().setThumbnail(container.getTask(), thumbnailData);
}
}
}
return null;
@ -812,7 +825,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
mActivity.addMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
mSyncTransactionApplier = new SurfaceTransactionApplier(this);
mLiveTileParams.setSyncTransactionApplier(mSyncTransactionApplier);
runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.mTransformParams
.setSyncTransactionApplier(mSyncTransactionApplier));
RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this);
mIPipAnimationListener.setActivityAndRecentsView(mActivity, this);
SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(
@ -830,7 +844,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
mActivity.removeMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
mSyncTransactionApplier = null;
mLiveTileParams.setSyncTransactionApplier(null);
runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.mTransformParams
.setSyncTransactionApplier(null));
executeSideTaskLaunchCallback();
RecentsModel.INSTANCE.get(getContext()).removeThumbnailChangeListener(this);
SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(null);
@ -851,9 +866,15 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
if (child instanceof TaskView && child != mSplitHiddenTaskView
&& child != mMovingTaskView) {
TaskView taskView = (TaskView) child;
mHasVisibleTaskData.delete(taskView.getTaskIds()[0]);
for (int i : taskView.getTaskIds()) {
mHasVisibleTaskData.delete(i);
}
if (child instanceof GroupedTaskView) {
mGroupedTaskViewPool.recycle((GroupedTaskView)taskView);
} else {
mTaskViewPool.recycle(taskView);
}
taskView.setTaskViewId(-1);
mTaskViewPool.recycle(taskView);
mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, getTaskViewCount() == 0);
}
updateTaskStartIndex(child);
@ -906,10 +927,15 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
}
}
/**
* TODO(b/195675206) Check both taskIDs from runningTaskViewId
* and launch if either of them is {@param taskId}
*/
public void launchSideTaskInLiveTileModeForRestartedApp(int taskId) {
int runningTaskViewId = getTaskViewIdFromTaskId(taskId);
if (mRunningTaskViewId != -1 && mRunningTaskViewId == runningTaskViewId) {
RemoteAnimationTargets targets = getLiveTileParams().getTargetSet();
TransformParams params = mRemoteTargetHandles[0].mTransformParams;
RemoteAnimationTargets targets = params.getTargetSet();
if (targets != null && targets.findTask(taskId) != null) {
launchSideTaskInLiveTileMode(taskId, targets.apps, targets.wallpapers,
targets.nonApps);
@ -1015,10 +1041,21 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
if (!enabled) {
// Reset the running task when leaving overview since it can still have a reference to
// its thumbnail
mTmpRunningTask = null;
mTmpRunningTasks = null;
if (mSplitSelectStateController.isSplitSelectActive()) {
cancelSplitSelect(false);
}
// Remove grouped tasks and recycle once we exit overview
int taskCount = getTaskViewCount();
for (int i = 0; i < taskCount; i++) {
View v = getTaskViewAt(i);
if (!(v instanceof GroupedTaskView)) {
return;
}
GroupedTaskView gtv = (GroupedTaskView) v;
gtv.onTaskListVisibilityChanged(false);
removeView(gtv);
}
}
updateLocusId();
}
@ -1220,18 +1257,32 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
TaskView ignoreResetTaskView =
mIgnoreResetTaskId == -1 ? null : getTaskViewByTaskId(mIgnoreResetTaskId);
final int requiredTaskCount = tasks.size();
if (getTaskViewCount() != requiredTaskCount) {
int[] splitTaskIds =
LauncherSplitScreenListener.INSTANCE.getNoCreate().getSplitTaskIds();
int requiredGroupTaskViews = splitTaskIds.length / 2;
// Subtract half the number of split tasks and not total number because we've already
// added a GroupedTaskView when swipe up gesture happens.
// This will need to change if we start showing GroupedTaskViews during swipe up from home
int requiredTaskViewCount = tasks.size() - requiredGroupTaskViews;
if (getTaskViewCount() != requiredTaskViewCount) {
if (indexOfChild(mClearAllButton) != -1) {
removeView(mClearAllButton);
}
for (int i = getTaskViewCount(); i < requiredTaskCount; i++) {
addView(getTaskViewFromPool());
for (int i = getTaskViewCount(); i < requiredTaskViewCount; i++) {
addView(getTaskViewFromPool(false));
}
while (getTaskViewCount() > requiredTaskCount) {
while (getTaskViewCount() > requiredTaskViewCount) {
removeView(getChildAt(getChildCount() - 1));
}
if (requiredTaskCount > 0) {
while (requiredGroupTaskViews > 0) {
// Add to front of list
addView(getTaskViewFromPool(true), 0);
requiredGroupTaskViews--;
}
if (requiredTaskViewCount > 0) {
addView(mClearAllButton);
}
}
@ -1245,12 +1296,28 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
+ " runningTaskViewId: " + mRunningTaskViewId
+ " forTaskView: " + getTaskViewFromTaskViewId(mRunningTaskViewId));
// Rebind and reset all task views
for (int i = requiredTaskCount - 1; i >= 0; i--) {
final int pageIndex = requiredTaskCount - i - 1 + mTaskViewStartIndex;
final Task task = tasks.get(i);
for (int taskViewIndex = requiredTaskViewCount - 1, taskDataIndex = tasks.size() - 1;
taskViewIndex >= 0;
taskViewIndex--, taskDataIndex--) {
final int pageIndex = requiredTaskViewCount - taskViewIndex - 1 + mTaskViewStartIndex;
final Task task = tasks.get(taskDataIndex);
final TaskView taskView = (TaskView) getChildAt(pageIndex);
taskView.bind(task, mOrientationState);
if (taskView instanceof GroupedTaskView) {
Task leftTop;
Task rightBottom;
if (task.key.id == splitTaskIds[0]) {
leftTop = task;
taskDataIndex--;
rightBottom = tasks.get(taskDataIndex);
} else {
rightBottom = task;
taskDataIndex--;
leftTop = tasks.get(taskDataIndex);
}
((GroupedTaskView) taskView).bind(leftTop, rightBottom, mOrientationState);
} else {
taskView.bind(task, mOrientationState);
}
}
// Keep same previous focused task
@ -1270,8 +1337,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
newRunningTaskView = getTaskViewByTaskId(runningTaskId);
if (newRunningTaskView == null) {
StringBuilder sb = new StringBuilder();
for (int i = requiredTaskCount - 1; i >= 0; i--) {
final int pageIndex = requiredTaskCount - i - 1 + mTaskViewStartIndex;
for (int i = requiredTaskViewCount - 1; i >= 0; i--) {
final int pageIndex = requiredTaskViewCount - i - 1 + mTaskViewStartIndex;
final TaskView taskView = (TaskView) getChildAt(pageIndex);
int taskViewId = taskView.getTaskViewId();
sb.append(" taskViewId: " + taskViewId
@ -1355,12 +1422,12 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
// Since we reuse the same mLiveTileTaskViewSimulator in the RecentsView, we need
// to reset the params after it settles in Overview from swipe up so that we don't
// render with obsolete param values.
mLiveTileTaskViewSimulator.taskPrimaryTranslation.value = 0;
mLiveTileTaskViewSimulator.taskSecondaryTranslation.value = 0;
mLiveTileTaskViewSimulator.fullScreenProgress.value = 0;
mLiveTileTaskViewSimulator.recentsViewScale.value = 1;
mLiveTileParams.setTargetAlpha(1);
runActionOnRemoteHandles(remoteTargetHandle -> {
remoteTargetHandle.mTaskViewSimulator.taskPrimaryTranslation.value = 0;
remoteTargetHandle.mTaskViewSimulator.taskSecondaryTranslation.value = 0;
remoteTargetHandle.mTaskViewSimulator.fullScreenProgress.value = 0;
remoteTargetHandle.mTaskViewSimulator.recentsViewScale.value = 1;
});
// Similar to setRunningTaskHidden below, reapply the state before runningTaskView is
// null.
@ -1414,7 +1481,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
setPageSpacing(dp.overviewPageSpacing);
// Propagate DeviceProfile change event.
mLiveTileTaskViewSimulator.setDp(dp);
runActionOnRemoteHandles(
remoteTargetHandle -> remoteTargetHandle.mTaskViewSimulator.setDp(dp));
mActionsView.setDp(dp);
mOrientationState.setDeviceProfile(dp);
@ -1525,8 +1593,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
}
public void getTaskSize(Rect outRect) {
mSizeStrategy.calculateTaskSize(mActivity, mActivity.getDeviceProfile(), outRect,
mOrientationHandler);
mSizeStrategy.calculateTaskSize(mActivity, mActivity.getDeviceProfile(), outRect);
mLastComputedTaskSize.set(outRect);
}
@ -1534,8 +1601,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
* Returns the size of task selected to enter modal state.
*/
public Point getSelectedTaskSize() {
mSizeStrategy.calculateTaskSize(mActivity, mActivity.getDeviceProfile(), mTempRect,
mOrientationHandler);
mSizeStrategy.calculateTaskSize(mActivity, mActivity.getDeviceProfile(), mTempRect);
return new Point(mTempRect.width(), mTempRect.height());
}
@ -1668,8 +1734,17 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
visible = lower <= index && index <= upper;
}
if (visible) {
if (task == mTmpRunningTask) {
// Skip loading if this is the task that we are animating into
boolean skipLoadingTask = false;
if (mTmpRunningTasks != null) {
for (Task t : mTmpRunningTasks) {
if (task == t) {
// Skip loading if this is the task that we are animating into
skipLoadingTask = true;
break;
}
}
}
if (skipLoadingTask) {
continue;
}
if (!mHasVisibleTaskData.get(task.key.id)) {
@ -1744,7 +1819,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
}
}
setEnableDrawingLiveTile(false);
mLiveTileParams.setTargetSet(null);
runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.mTransformParams
.setTargetSet(null));
// These are relatively expensive and don't need to be done this frame (RecentsView isn't
// visible anyway), so defer by a frame to get off the critical path, e.g. app to home.
@ -1811,8 +1887,10 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
* Handle the edge case where Recents could increment task count very high over long
* period of device usage. Probably will never happen, but meh.
*/
private TaskView getTaskViewFromPool() {
TaskView taskView = mTaskViewPool.getView();
private <T extends TaskView> T getTaskViewFromPool(boolean isGrouped) {
T taskView = isGrouped ?
(T) mGroupedTaskViewPool.getView() :
(T) mTaskViewPool.getView();
taskView.setTaskViewId(mTaskViewIdCount);
if (mTaskViewIdCount == Integer.MAX_VALUE) {
mTaskViewIdCount = 0;
@ -1848,7 +1926,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
/**
* Called when a gesture from an app is starting.
*/
public void onGestureAnimationStart(RunningTaskInfo runningTaskInfo) {
public void onGestureAnimationStart(RunningTaskInfo[] runningTaskInfo) {
mGestureActive = true;
// This needs to be called before the other states are set since it can create the task view
if (mOrientationState.setGestureActive(true)) {
@ -1915,7 +1993,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
*/
public void onPrepareGestureEndAnimation(
@Nullable AnimatorSet animatorSet, GestureState.GestureEndTarget endTarget,
TaskViewSimulator taskViewSimulator) {
TaskViewSimulator[] taskViewSimulators) {
mCurrentGestureEndTarget = endTarget;
if (endTarget == GestureState.GestureEndTarget.RECENTS) {
setEnableFreeScroll(true);
@ -1932,13 +2010,16 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
runningTaskView.getGridTranslationX(),
runningTaskView.getGridTranslationY());
}
if (animatorSet == null) {
setGridProgress(1);
taskViewSimulator.taskPrimaryTranslation.value = runningTaskPrimaryGridTranslation;
} else {
animatorSet.play(ObjectAnimator.ofFloat(this, RECENTS_GRID_PROGRESS, 1));
animatorSet.play(taskViewSimulator.taskPrimaryTranslation.animateToValue(
runningTaskPrimaryGridTranslation));
for (TaskViewSimulator tvs : taskViewSimulators) {
if (animatorSet == null) {
setGridProgress(1);
tvs.taskPrimaryTranslation.value =
runningTaskPrimaryGridTranslation;
} else {
animatorSet.play(ObjectAnimator.ofFloat(this, RECENTS_GRID_PROGRESS, 1));
animatorSet.play(tvs.taskPrimaryTranslation.animateToValue(
runningTaskPrimaryGridTranslation));
}
}
}
}
@ -1967,7 +2048,21 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
/**
* Returns true if we should add a stub taskView for the running task id
*/
protected boolean shouldAddStubTaskView(RunningTaskInfo runningTaskInfo) {
protected boolean shouldAddStubTaskView(RunningTaskInfo[] runningTaskInfos) {
if (runningTaskInfos.length > 1) {
// * Always create new view for GroupedTaskView
// * Remove existing associated taskViews for tasks currently in split
for (RunningTaskInfo rti : runningTaskInfos) {
TaskView taskView = getTaskViewByTaskId(rti.taskId);
if (taskView == null) {
continue;
}
taskView.onTaskListVisibilityChanged(false);
removeView(taskView);
}
return true;
}
RunningTaskInfo runningTaskInfo = runningTaskInfos[0];
return runningTaskInfo != null && getTaskViewByTaskId(runningTaskInfo.taskId) == null;
}
@ -1977,29 +2072,44 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
* All subsequent calls to reload will keep the task as the first item until {@link #reset()}
* is called. Also scrolls the view to this task.
*/
public void showCurrentTask(RunningTaskInfo runningTaskInfo) {
public void showCurrentTask(RunningTaskInfo[] runningTaskInfo) {
int runningTaskViewId = -1;
boolean needGroupTaskView = runningTaskInfo.length > 1;
RunningTaskInfo taskInfo = runningTaskInfo[0];
if (shouldAddStubTaskView(runningTaskInfo)) {
boolean wasEmpty = getChildCount() == 0;
// Add an empty view for now until the task plan is loaded and applied
final TaskView taskView = getTaskViewFromPool();
final TaskView taskView;
if (needGroupTaskView) {
taskView = getTaskViewFromPool(true);
RunningTaskInfo secondaryTaskInfo = runningTaskInfo[1];
mTmpRunningTasks = new Task[]{
Task.from(new TaskKey(taskInfo), taskInfo, false),
Task.from(new TaskKey(secondaryTaskInfo), secondaryTaskInfo, false)
};
addView(taskView, mTaskViewStartIndex);
((GroupedTaskView)taskView).bind(mTmpRunningTasks[0], mTmpRunningTasks[1],
mOrientationState);
} else {
taskView = getTaskViewFromPool(false);
addView(taskView, mTaskViewStartIndex);
// The temporary running task is only used for the duration between the start of the
// gesture and the task list is loaded and applied
mTmpRunningTasks = new Task[]{Task.from(new TaskKey(taskInfo), taskInfo, false)};
taskView.bind(mTmpRunningTasks[0], mOrientationState);
}
runningTaskViewId = taskView.getTaskViewId();
addView(taskView, mTaskViewStartIndex);
if (wasEmpty) {
addView(mClearAllButton);
}
// The temporary running task is only used for the duration between the start of the
// gesture and the task list is loaded and applied
mTmpRunningTask = Task.from(new TaskKey(runningTaskInfo), runningTaskInfo, false);
taskView.bind(mTmpRunningTask, mOrientationState);
// Measure and layout immediately so that the scroll values is updated instantly
// as the user might be quick-switching
measure(makeMeasureSpec(getMeasuredWidth(), EXACTLY),
makeMeasureSpec(getMeasuredHeight(), EXACTLY));
layout(getLeft(), getTop(), getRight(), getBottom());
} else if (getTaskViewByTaskId(runningTaskInfo.taskId) != null) {
runningTaskViewId = getTaskViewByTaskId(runningTaskInfo.taskId).getTaskViewId();
} else if (!needGroupTaskView && getTaskViewByTaskId(taskInfo.taskId) != null) {
runningTaskViewId = getTaskViewByTaskId(taskInfo.taskId).getTaskViewId();
}
boolean runningTaskTileHidden = mRunningTaskTileHidden;
@ -2416,8 +2526,11 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
// Use setFloat instead of setViewAlpha as we want to keep the view visible even when it's
// alpha is set to 0 so that it can be recycled in the view pool properly
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && taskView.isRunningTask()) {
anim.setFloat(mLiveTileParams, TransformParams.TARGET_ALPHA, 0,
clampToProgress(ACCEL, 0, 0.5f));
runActionOnRemoteHandles(remoteTargetHandle -> {
TransformParams params = remoteTargetHandle.mTransformParams;
anim.setFloat(params, TransformParams.TARGET_ALPHA, 0,
clampToProgress(ACCEL, 0, 0.5f));
});
}
anim.setFloat(taskView, VIEW_ALPHA, 0, clampToProgress(ACCEL, 0, 0.5f));
FloatProperty<TaskView> secondaryViewTranslate =
@ -2436,10 +2549,12 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile
&& taskView.isRunningTask()) {
anim.addOnFrameCallback(() -> {
mLiveTileTaskViewSimulator.taskSecondaryTranslation.value =
mOrientationHandler.getSecondaryValue(
taskView.getTranslationX(),
taskView.getTranslationY());
runActionOnRemoteHandles(
remoteTargetHandle -> remoteTargetHandle.mTaskViewSimulator
.taskSecondaryTranslation.value = mOrientationHandler
.getSecondaryValue(taskView.getTranslationX(),
taskView.getTranslationY()
));
redrawLiveTile();
});
}
@ -2490,7 +2605,6 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
boolean showAsGrid = showAsGrid();
int taskCount = getTaskViewCount();
int dismissedIndex = indexOfChild(dismissedTaskView);
int dismissedTaskId = dismissedTaskView.getTaskIds()[0];
int dismissedTaskViewId = dismissedTaskView.getTaskViewId();
// Grid specific properties.
@ -2588,9 +2702,14 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
&& child instanceof TaskView
&& ((TaskView) child).isRunningTask()) {
anim.addOnFrameCallback(() -> {
mLiveTileTaskViewSimulator.taskPrimaryTranslation.value =
mOrientationHandler.getPrimaryValue(child.getTranslationX(),
child.getTranslationY());
runActionOnRemoteHandles(
remoteTargetHandle ->
remoteTargetHandle.mTaskViewSimulator
.taskPrimaryTranslation.value =
mOrientationHandler.getPrimaryValue(
child.getTranslationX(),
child.getTranslationY()
));
redrawLiveTile();
});
}
@ -2672,9 +2791,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
if (ENABLE_QUICKSTEP_LIVE_TILE.get()
&& dismissedTaskView.isRunningTask()) {
finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
() -> removeTaskInternal(dismissedTaskId));
() -> removeTaskInternal(dismissedTaskViewId));
} else {
removeTaskInternal(dismissedTaskId);
removeTaskInternal(dismissedTaskViewId);
}
mActivity.getStatsLogManager().logger()
.withItemInfo(dismissedTaskView.getItemInfo())
@ -2804,9 +2923,17 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
return lastVisibleIndex;
}
private void removeTaskInternal(int dismissedTaskId) {
private void removeTaskInternal(int dismissedTaskViewId) {
int[] taskIds = getTaskIdsForTaskViewId(dismissedTaskViewId);
int primaryTaskId = taskIds[0];
int secondaryTaskId = taskIds[1];
UI_HELPER_EXECUTOR.getHandler().postDelayed(
() -> ActivityManagerWrapper.getInstance().removeTask(dismissedTaskId),
() -> {
ActivityManagerWrapper.getInstance().removeTask(primaryTaskId);
if (secondaryTaskId != -1) {
ActivityManagerWrapper.getInstance().removeTask(secondaryTaskId);
}
},
REMOVE_TASK_WAIT_FOR_APP_STOP_MS);
}
@ -3139,7 +3266,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
mLastComputedTaskStartPushOutDistance = null;
mLastComputedTaskEndPushOutDistance = null;
updatePageOffsets();
mLiveTileTaskViewSimulator.setScroll(getScrollOffset());
runActionOnRemoteHandles(
remoteTargetHandle -> remoteTargetHandle.mTaskViewSimulator
.setScroll(getScrollOffset()));
setImportantForAccessibility(isModal() ? IMPORTANT_FOR_ACCESSIBILITY_NO
: IMPORTANT_FOR_ACCESSIBILITY_AUTO);
}
@ -3203,7 +3332,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
translationProperty.set(child, totalTranslation);
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile
&& i == getRunningTaskIndex()) {
mLiveTileTaskViewSimulator.taskPrimaryTranslation.value = totalTranslation;
runActionOnRemoteHandles(
remoteTargetHandle -> remoteTargetHandle.mTaskViewSimulator
.taskPrimaryTranslation.value = totalTranslation);
redrawLiveTile();
}
}
@ -3306,7 +3437,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
TaskView task = getTaskViewAt(i);
task.getTaskResistanceTranslationProperty().set(task, translation / getScaleY());
}
mLiveTileTaskViewSimulator.recentsViewSecondaryTranslation.value = translation;
runActionOnRemoteHandles(
remoteTargetHandle -> remoteTargetHandle.mTaskViewSimulator
.recentsViewSecondaryTranslation.value = translation);
}
protected void setTaskViewsPrimarySplitTranslation(float translation) {
@ -3508,8 +3641,6 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
resetTaskVisuals();
mSplitHiddenTaskView.setVisibility(VISIBLE);
mSplitHiddenTaskView = null;
mSecondSplitHiddenTaskView.setVisibility(VISIBLE);
mSecondSplitHiddenTaskView = null;
mSplitHiddenTaskViewIndex = -1;
if (mFirstFloatingTaskView != null) {
mActivity.getRootView().removeView(mFirstFloatingTaskView);
@ -3518,6 +3649,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
if (mSecondFloatingTaskView != null) {
mActivity.getRootView().removeView(mSecondFloatingTaskView);
mSecondFloatingTaskView = null;
mSecondSplitHiddenTaskView.setVisibility(VISIBLE);
mSecondSplitHiddenTaskView = null;
}
}
@ -3613,10 +3746,12 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
int runningTaskIndex = recentsView.getRunningTaskIndex();
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && runningTaskIndex != -1
&& runningTaskIndex != taskIndex) {
anim.play(ObjectAnimator.ofFloat(
recentsView.getLiveTileTaskViewSimulator().taskPrimaryTranslation,
AnimatedFloat.VALUE,
primaryTranslation));
for (RemoteTargetHandle remoteHandle : recentsView.getRemoteTargetHandles()) {
anim.play(ObjectAnimator.ofFloat(
remoteHandle.mTaskViewSimulator.taskPrimaryTranslation,
AnimatedFloat.VALUE,
primaryTranslation));
}
}
int otherAdjacentTaskIndex = centerTaskIndex + (centerTaskIndex - taskIndex);
@ -3696,7 +3831,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
mPendingAnimation = new PendingAnimation(duration);
mPendingAnimation.add(anim);
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
mLiveTileTaskViewSimulator.addOverviewToAppAnim(mPendingAnimation, interpolator);
runActionOnRemoteHandles(
remoteTargetHandle -> remoteTargetHandle.mTaskViewSimulator
.addOverviewToAppAnim(mPendingAnimation, interpolator));
mPendingAnimation.addOnFrameCallback(this::redrawLiveTile);
}
mPendingAnimation.addEndListener(isSuccess -> {
@ -3787,31 +3924,87 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
}
public void redrawLiveTile() {
if (mLiveTileParams.getTargetSet() != null) {
mLiveTileTaskViewSimulator.apply(mLiveTileParams);
}
runActionOnRemoteHandles(remoteTargetHandle -> {
TransformParams params = remoteTargetHandle.mTransformParams;
if (params.getTargetSet() != null) {
remoteTargetHandle.mTaskViewSimulator.apply(params);
}
});
}
public TaskViewSimulator getLiveTileTaskViewSimulator() {
return mLiveTileTaskViewSimulator;
}
public TransformParams getLiveTileParams() {
return mLiveTileParams;
public RemoteTargetHandle[] getRemoteTargetHandles() {
return mRemoteTargetHandles;
}
// TODO: To be removed in a follow up CL
public void setRecentsAnimationTargets(RecentsAnimationController recentsAnimationController,
RecentsAnimationTargets recentsAnimationTargets) {
mRecentsAnimationController = recentsAnimationController;
if (recentsAnimationTargets != null && recentsAnimationTargets.apps.length > 0) {
if (mSyncTransactionApplier != null) {
recentsAnimationTargets.addReleaseCheck(mSyncTransactionApplier);
}
mLiveTileTaskViewSimulator.setPreview(
recentsAnimationTargets.apps[recentsAnimationTargets.apps.length - 1]);
mLiveTileParams.setTargetSet(recentsAnimationTargets);
if (recentsAnimationTargets == null || recentsAnimationTargets.apps.length == 0) {
return;
}
if (mSyncTransactionApplier != null) {
recentsAnimationTargets.addReleaseCheck(mSyncTransactionApplier);
}
// TODO Consolidate this shared code with SwipeUpAnimationLogic (or mabe just reuse
// what that class has and pass it into here
mRemoteTargetHandles = new RemoteTargetHandle[recentsAnimationTargets.apps.length];
TaskViewSimulator primaryTvs = createTaskViewSimulator();
mRemoteTargetHandles[0] = new RemoteTargetHandle(primaryTvs, new TransformParams());
if (recentsAnimationTargets.apps.length == 1) {
mRemoteTargetHandles[0].mTaskViewSimulator
.setPreview(recentsAnimationTargets.apps[0], null);
mRemoteTargetHandles[0].mTransformParams.setTargetSet(recentsAnimationTargets);
} else {
TaskViewSimulator secondaryTvs = createTaskViewSimulator();
secondaryTvs.setOrientationState(mOrientationState);
secondaryTvs.recentsViewScale.value = 1;
mRemoteTargetHandles[1] = new RemoteTargetHandle(secondaryTvs, new TransformParams());
RemoteAnimationTargetCompat dividerTarget =
recentsAnimationTargets.getNonAppTargetOfType(TYPE_DOCK_DIVIDER);
RemoteAnimationTargetCompat primaryTaskTarget = recentsAnimationTargets.apps[0];
RemoteAnimationTargetCompat secondaryTaskTarget = recentsAnimationTargets.apps[1];
SplitConfigurationOptions.StagedSplitBounds
info = new SplitConfigurationOptions.StagedSplitBounds(
primaryTaskTarget.screenSpaceBounds,
secondaryTaskTarget.screenSpaceBounds, dividerTarget.screenSpaceBounds);
mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(primaryTaskTarget, info);
mRemoteTargetHandles[1].mTaskViewSimulator.setPreview(secondaryTaskTarget, info);
RemoteAnimationTargets rats = new RemoteAnimationTargets(
new RemoteAnimationTargetCompat[]{primaryTaskTarget},
recentsAnimationTargets.wallpapers, recentsAnimationTargets.nonApps,
MODE_CLOSING
);
RemoteAnimationTargets splitRats = new RemoteAnimationTargets(
new RemoteAnimationTargetCompat[]{secondaryTaskTarget},
recentsAnimationTargets.wallpapers, recentsAnimationTargets.nonApps,
MODE_CLOSING
);
mRemoteTargetHandles[0].mTransformParams.setTargetSet(rats);
mRemoteTargetHandles[1].mTransformParams.setTargetSet(splitRats);
}
}
/** Helper to avoid writing some for-loops to iterate over {@link #mRemoteTargetHandles} */
private void runActionOnRemoteHandles(Consumer<RemoteTargetHandle> consumer) {
if (mRemoteTargetHandles == null) {
return;
}
for (RemoteTargetHandle handle : mRemoteTargetHandles) {
consumer.accept(handle);
}
}
private TaskViewSimulator createTaskViewSimulator() {
TaskViewSimulator tvs = new TaskViewSimulator(getContext(), getSizeStrategy());
tvs.setOrientationState(mOrientationState);
tvs.setDp(mActivity.getDeviceProfile());
tvs.recentsViewScale.value = 1;
return tvs;
}
public void finishRecentsAnimation(boolean toRecents, Runnable onFinishComplete) {
@ -4117,14 +4310,42 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
}
return;
}
int runningTaskId = getTaskIdsForRunningTaskView()[0];
switchToScreenshot(mRunningTaskViewId == -1 ? null
: mRecentsAnimationController.screenshotTask(runningTaskId), onFinishRunnable);
switchToScreenshotInternal(onFinishRunnable);
}
private void switchToScreenshotInternal(Runnable onFinishRunnable) {
TaskView taskView = getRunningTaskView();
if (taskView == null) {
onFinishRunnable.run();
return;
}
taskView.setShowScreenshot(true);
for (TaskView.TaskIdAttributeContainer container :
taskView.getTaskIdAttributeContainers()) {
if (container == null) {
continue;
}
ThumbnailData td =
mRecentsAnimationController.screenshotTask(container.getTask().key.id);
TaskThumbnailView thumbnailView = container.getThumbnailView();
if (td != null) {
thumbnailView.setThumbnail(container.getTask(), td);
} else {
thumbnailView.refresh();
}
}
ViewUtils.postFrameDrawn(taskView, onFinishRunnable);
}
/**
* Switch the current running task view to static snapshot mode, using the
* provided thumbnail data as the snapshot.
* TODO(b/195609063) Consolidate this method w/ the one above, except this thumbnail data comes
* from gesture state, which is a larger change of it having to keep track of multiple tasks.
* OR. Maybe it doesn't need to pass in a thumbnail and we can use the exact same flow as above
*/
public void switchToScreenshot(ThumbnailData thumbnailData, Runnable onFinishRunnable) {
TaskView taskView = getRunningTaskView();
@ -4277,7 +4498,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
}
private void dispatchScrollChanged() {
mLiveTileTaskViewSimulator.setScroll(getScrollOffset());
runActionOnRemoteHandles(remoteTargetHandle ->
remoteTargetHandle.mTaskViewSimulator.setScroll(getScrollOffset()));
for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
mScrollListeners.get(i).onScrollChanged();
}

View File

@ -91,6 +91,7 @@ import com.android.launcher3.util.TransformingTouchDelegate;
import com.android.launcher3.util.ViewPool.Reusable;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.RemoteAnimationTargets;
import com.android.quickstep.SwipeUpAnimationLogic.RemoteTargetHandle;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskIconCache;
import com.android.quickstep.TaskOverlayFactory;
@ -100,16 +101,20 @@ import com.android.quickstep.TaskViewUtils;
import com.android.quickstep.util.CancellableTask;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.util.TaskCornerRadius;
import com.android.quickstep.util.TransformParams;
import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.lang.annotation.Retention;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Stream;
/**
* A task in the Recents view.
@ -333,8 +338,8 @@ public class TaskView extends FrameLayout implements Reusable {
private final TaskOutlineProvider mOutlineProvider;
private Task mTask;
private TaskThumbnailView mSnapshotView;
protected Task mTask;
protected TaskThumbnailView mSnapshotView;
private IconView mIconView;
private final DigitalWellBeingToast mDigitalWellBeingToast;
private float mFullscreenProgress;
@ -342,7 +347,7 @@ public class TaskView extends FrameLayout implements Reusable {
private float mNonGridScale = 1;
private float mDismissScale = 1;
private final FullscreenDrawParams mCurrentFullscreenParams;
private final StatefulActivity mActivity;
protected final StatefulActivity mActivity;
// Various causes of changing primary translation, which we aggregate to setTranslationX/Y().
private float mDismissTranslationX;
@ -371,7 +376,12 @@ public class TaskView extends FrameLayout implements Reusable {
private float mStableAlpha = 1;
private int mTaskViewId = -1;
private final int[] mTaskIdContainer = new int[]{-1, -1};
/**
* 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 final TaskIdAttributeContainer[] mTaskIdAttributeContainer =
new TaskIdAttributeContainer[2];
private boolean mShowScreenshot;
@ -522,10 +532,15 @@ public class TaskView extends FrameLayout implements Reusable {
cancelPendingLoadTasks();
mTask = task;
mTaskIdContainer[0] = mTask.key.id;
mTaskIdAttributeContainer[0] = new TaskIdAttributeContainer(task, mSnapshotView);
mSnapshotView.bind(task);
setOrientationState(orientedState);
}
public TaskIdAttributeContainer[] getTaskIdAttributeContainers() {
return mTaskIdAttributeContainer;
}
public Task getTask() {
return mTask;
}
@ -567,7 +582,29 @@ public class TaskView extends FrameLayout implements Reusable {
mIsClickableAsLiveTile = false;
RecentsView recentsView = getRecentsView();
final RemoteAnimationTargets targets = recentsView.getLiveTileParams().getTargetSet();
RemoteAnimationTargets targets;
RemoteTargetHandle[] remoteTargetHandles =
recentsView.mRemoteTargetHandles;
if (remoteTargetHandles.length == 1) {
targets = remoteTargetHandles[0].mTransformParams.getTargetSet();
} else {
TransformParams topLeftParams = remoteTargetHandles[0].mTransformParams;
TransformParams rightBottomParams = remoteTargetHandles[1].mTransformParams;
RemoteAnimationTargetCompat[] apps = Stream.concat(
Arrays.stream(topLeftParams.getTargetSet().apps),
Arrays.stream(rightBottomParams.getTargetSet().apps))
.toArray(RemoteAnimationTargetCompat[]::new);
RemoteAnimationTargetCompat[] wallpapers = Stream.concat(
Arrays.stream(topLeftParams.getTargetSet().wallpapers),
Arrays.stream(rightBottomParams.getTargetSet().wallpapers))
.toArray(RemoteAnimationTargetCompat[]::new);
RemoteAnimationTargetCompat[] nonApps = Stream.concat(
Arrays.stream(topLeftParams.getTargetSet().nonApps),
Arrays.stream(rightBottomParams.getTargetSet().nonApps))
.toArray(RemoteAnimationTargetCompat[]::new);
targets = new RemoteAnimationTargets(apps, wallpapers, nonApps,
topLeftParams.getTargetSet().targetMode);
}
if (targets == null) {
// If the recents animation is cancelled somehow between the parent if block and
// here, try to launch the task as a non live tile task.
@ -727,11 +764,11 @@ public class TaskView extends FrameLayout implements Reusable {
}
}
private boolean needsUpdate(@TaskDataChanges int dataChange, @TaskDataChanges int flag) {
protected boolean needsUpdate(@TaskDataChanges int dataChange, @TaskDataChanges int flag) {
return (dataChange & flag) == flag;
}
private void cancelPendingLoadTasks() {
protected void cancelPendingLoadTasks() {
if (mThumbnailLoadRequest != null) {
mThumbnailLoadRequest.cancel();
mThumbnailLoadRequest = null;
@ -1517,4 +1554,22 @@ public class TaskView extends FrameLayout implements Reusable {
}
}
public class TaskIdAttributeContainer {
private final TaskThumbnailView thumbnailView;
private final Task task;
public TaskIdAttributeContainer(Task task, TaskThumbnailView thumbnailView) {
this.task = task;
this.thumbnailView = thumbnailView;
}
public TaskThumbnailView getThumbnailView() {
return thumbnailView;
}
public Task getTask() {
return task;
}
}
}

View File

@ -219,6 +219,7 @@ public class DeviceProfile {
// DragController
public int flingToDeleteThresholdVelocity;
/** TODO: Once we fully migrate to staged split, remove "isMultiWindowMode" */
DeviceProfile(Context context, InvariantDeviceProfile inv, Info info, WindowBounds windowBounds,
boolean isMultiWindowMode, boolean transposeLayoutWithOrientation,
boolean useTwoPanels) {

View File

@ -18,6 +18,8 @@ package com.android.launcher3.util;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.graphics.Rect;
import androidx.annotation.IntDef;
import java.lang.annotation.Retention;
@ -82,4 +84,25 @@ public final class SplitConfigurationOptions {
mStageType = stageType;
}
}
public static class StagedSplitBounds {
public final Rect mLeftTopBounds;
public final Rect mRightBottomBounds;
public final Rect mDividerBounds;
public StagedSplitBounds(Rect leftTopBounds, Rect rightBottomBounds, Rect dividerBounds) {
mLeftTopBounds = leftTopBounds;
mRightBottomBounds = rightBottomBounds;
mDividerBounds = dividerBounds;
}
}
public static class StagedSplitTaskPosition {
public int taskId = -1;
@StagePosition
public int stagePosition = STAGE_POSITION_UNDEFINED;
@StageType
public int stageType = STAGE_TYPE_UNDEFINED;
}
}