Merge "Add funtional animation for SplitSelect from Grid." into sc-dev am: 46d420bf14

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/14373811

Change-Id: I87348741e8c71cf8a445a42156ff03f17f806d92
This commit is contained in:
TreeHugger Robot
2021-05-05 19:29:45 +00:00
committed by Automerger Merge Worker
9 changed files with 255 additions and 75 deletions
+7 -6
View File
@@ -15,12 +15,6 @@
--> -->
<merge xmlns:android="http://schemas.android.com/apk/res/android"> <merge xmlns:android="http://schemas.android.com/apk/res/android">
<com.android.quickstep.views.SplitPlaceholderView
android:id="@+id/split_placeholder"
android:layout_width="match_parent"
android:layout_height="@dimen/split_placeholder_size"
android:background="@android:color/darker_gray"
android:visibility="gone" />
<com.android.quickstep.views.LauncherRecentsView <com.android.quickstep.views.LauncherRecentsView
android:id="@+id/overview_panel" android:id="@+id/overview_panel"
@@ -31,6 +25,13 @@
android:clipToPadding="false" android:clipToPadding="false"
android:visibility="invisible" /> android:visibility="invisible" />
<com.android.quickstep.views.SplitPlaceholderView
android:id="@+id/split_placeholder"
android:layout_width="match_parent"
android:layout_height="@dimen/split_placeholder_size"
android:background="@android:color/darker_gray"
android:visibility="gone" />
<include <include
android:id="@+id/overview_actions_view" android:id="@+id/overview_actions_view"
layout="@layout/overview_actions_container" /> layout="@layout/overview_actions_container" />
@@ -22,12 +22,12 @@ import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FA
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MODAL; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MODAL;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW; import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET; import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS; import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY; import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
import static com.android.quickstep.views.RecentsView.TASK_PRIMARY_TRANSLATION; import static com.android.quickstep.views.RecentsView.TASK_PRIMARY_SPLIT_TRANSLATION;
import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_SPLIT_TRANSLATION;
import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION; import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
import android.util.FloatProperty; import android.util.FloatProperty;
@@ -97,10 +97,10 @@ public abstract class BaseRecentsViewStateController<T extends RecentsView>
PagedOrientationHandler orientationHandler = PagedOrientationHandler orientationHandler =
((RecentsView) mLauncher.getOverviewPanel()).getPagedOrientationHandler(); ((RecentsView) mLauncher.getOverviewPanel()).getPagedOrientationHandler();
FloatProperty taskViewsFloat = orientationHandler.getSplitSelectTaskOffset( FloatProperty taskViewsFloat = orientationHandler.getSplitSelectTaskOffset(
TASK_PRIMARY_TRANSLATION, TASK_SECONDARY_TRANSLATION, mLauncher.getDeviceProfile()); TASK_PRIMARY_SPLIT_TRANSLATION, TASK_SECONDARY_SPLIT_TRANSLATION,
mLauncher.getDeviceProfile());
setter.setFloat(mRecentsView, taskViewsFloat, setter.setFloat(mRecentsView, taskViewsFloat,
toState.getOverviewSecondaryTranslation(mLauncher), toState.getOverviewSecondaryTranslation(mLauncher), LINEAR);
config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0, setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0,
config.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT)); config.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
@@ -25,6 +25,7 @@ import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITIO
import android.animation.AnimatorSet; import android.animation.AnimatorSet;
import android.app.ActivityOptions; import android.app.ActivityOptions;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Rect;
import android.os.Handler; import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.os.Looper; import android.os.Looper;
@@ -64,6 +65,7 @@ public class SplitSelectStateController {
private final SystemUiProxy mSystemUiProxy; private final SystemUiProxy mSystemUiProxy;
private TaskView mInitialTaskView; private TaskView mInitialTaskView;
private SplitPositionOption mInitialPosition; private SplitPositionOption mInitialPosition;
private Rect mInitialBounds;
private final Handler mHandler; private final Handler mHandler;
public SplitSelectStateController(Handler handler, SystemUiProxy systemUiProxy) { public SplitSelectStateController(Handler handler, SystemUiProxy systemUiProxy) {
@@ -74,9 +76,11 @@ public class SplitSelectStateController {
/** /**
* To be called after first task selected * To be called after first task selected
*/ */
public void setInitialTaskSelect(TaskView taskView, SplitPositionOption positionOption) { public void setInitialTaskSelect(TaskView taskView, SplitPositionOption positionOption,
Rect initialBounds) {
mInitialTaskView = taskView; mInitialTaskView = taskView;
mInitialPosition = positionOption; mInitialPosition = positionOption;
mInitialBounds = initialBounds;
} }
/** /**
@@ -220,9 +224,14 @@ public class SplitSelectStateController {
public void resetState() { public void resetState() {
mInitialTaskView = null; mInitialTaskView = null;
mInitialPosition = null; mInitialPosition = null;
mInitialBounds = null;
} }
public boolean isSplitSelectActive() { public boolean isSplitSelectActive() {
return mInitialTaskView != null; return mInitialTaskView != null;
} }
public Rect getInitialBounds() {
return mInitialBounds;
}
} }
@@ -245,8 +245,8 @@ public class LauncherRecentsView extends RecentsView<BaseQuickstepLauncher, Laun
if (mActivity.isInState(OVERVIEW_SPLIT_SELECT)) { if (mActivity.isInState(OVERVIEW_SPLIT_SELECT)) {
// We want to keep the tasks translations in this temporary state // We want to keep the tasks translations in this temporary state
// after resetting the rest above // after resetting the rest above
setTaskViewsResistanceTranslation(mTaskViewsSecondaryTranslation); setTaskViewsPrimarySplitTranslation(mTaskViewsPrimarySplitTranslation);
setTaskViewsPrimaryTranslation(mTaskViewsPrimaryTranslation); setTaskViewsSecondarySplitTranslation(mTaskViewsSecondarySplitTranslation);
} }
} }
@@ -254,16 +254,29 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
* more specific, we'd want to create a similar FloatProperty just for a TaskView's * more specific, we'd want to create a similar FloatProperty just for a TaskView's
* offsetX/Y property * offsetX/Y property
*/ */
public static final FloatProperty<RecentsView> TASK_PRIMARY_TRANSLATION = public static final FloatProperty<RecentsView> TASK_PRIMARY_SPLIT_TRANSLATION =
new FloatProperty<RecentsView>("taskPrimaryTranslation") { new FloatProperty<RecentsView>("taskPrimarySplitTranslation") {
@Override @Override
public void setValue(RecentsView recentsView, float v) { public void setValue(RecentsView recentsView, float v) {
recentsView.setTaskViewsPrimaryTranslation(v); recentsView.setTaskViewsPrimarySplitTranslation(v);
} }
@Override @Override
public Float get(RecentsView recentsView) { public Float get(RecentsView recentsView) {
return recentsView.mTaskViewsPrimaryTranslation; return recentsView.mTaskViewsPrimarySplitTranslation;
}
};
public static final FloatProperty<RecentsView> TASK_SECONDARY_SPLIT_TRANSLATION =
new FloatProperty<RecentsView>("taskSecondarySplitTranslation") {
@Override
public void setValue(RecentsView recentsView, float v) {
recentsView.setTaskViewsSecondarySplitTranslation(v);
}
@Override
public Float get(RecentsView recentsView) {
return recentsView.mTaskViewsSecondarySplitTranslation;
} }
}; };
@@ -279,7 +292,6 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
view.mLiveTileTaskViewSimulator.recentsViewScale.value = scale; view.mLiveTileTaskViewSimulator.recentsViewScale.value = scale;
view.updatePageOffsets(); view.updatePageOffsets();
view.setTaskViewsResistanceTranslation(view.mTaskViewsSecondaryTranslation); view.setTaskViewsResistanceTranslation(view.mTaskViewsSecondaryTranslation);
view.setTaskViewsPrimaryTranslation(view.mTaskViewsPrimaryTranslation);
} }
@Override @Override
@@ -363,7 +375,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
private float mAdjacentPageOffset = 0; private float mAdjacentPageOffset = 0;
protected float mTaskViewsSecondaryTranslation = 0; protected float mTaskViewsSecondaryTranslation = 0;
protected float mTaskViewsPrimaryTranslation = 0; protected float mTaskViewsPrimarySplitTranslation = 0;
protected float mTaskViewsSecondarySplitTranslation = 0;
// Progress from 0 to 1 where 0 is a carousel and 1 is a 2 row grid. // Progress from 0 to 1 where 0 is a carousel and 1 is a 2 row grid.
private float mGridProgress = 0; private float mGridProgress = 0;
private final IntSet mTopRowIdSet = new IntSet(); private final IntSet mTopRowIdSet = new IntSet();
@@ -1848,15 +1861,28 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
final int boxLength = Math.max(mLastComputedGridTaskSize.width(), final int boxLength = Math.max(mLastComputedGridTaskSize.width(),
mLastComputedGridTaskSize.height()); mLastComputedGridTaskSize.height());
int taskTopMargin = mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx; int taskTopMargin = mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx;
float heightOffset = (boxLength + taskTopMargin) + mRowSpacing;
float taskGridVerticalDiff = mLastComputedGridTaskSize.top - mLastComputedTaskSize.top; /*
* taskGridVerticalDiff is used to position the top of a task in the top row of the grid
* heightOffset is the vertical space one grid task takes + space between top and
* bottom row
* Summed together they provide the top position for bottom row of grid tasks
*/
final float taskGridVerticalDiff =
mLastComputedGridTaskSize.top - mLastComputedTaskSize.top;
final float heightOffset = (boxLength + taskTopMargin) + mRowSpacing;
int topRowWidth = 0; int topRowWidth = 0;
int bottomRowWidth = 0; int bottomRowWidth = 0;
float topAccumulatedTranslationX = 0; float topAccumulatedTranslationX = 0;
float bottomAccumulatedTranslationX = 0; float bottomAccumulatedTranslationX = 0;
// Contains whether the child index is in top or bottom of grid (for non-focused task)
// Different from mTopRowIdSet, which contains the taskId of what task is in top row
IntSet topSet = new IntSet(); IntSet topSet = new IntSet();
IntSet bottomSet = new IntSet(); IntSet bottomSet = new IntSet();
// Horizontal grid translation for each task
float[] gridTranslations = new float[taskCount]; float[] gridTranslations = new float[taskCount];
int focusedTaskIndex = Integer.MAX_VALUE; int focusedTaskIndex = Integer.MAX_VALUE;
@@ -1910,7 +1936,6 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
boolean isTopRow = isTaskDismissal ? mTopRowIdSet.contains(taskId) boolean isTopRow = isTaskDismissal ? mTopRowIdSet.contains(taskId)
: topRowWidth <= bottomRowWidth; : topRowWidth <= bottomRowWidth;
if (isTopRow) { if (isTopRow) {
gridTranslations[i] += topAccumulatedTranslationX;
topRowWidth += taskWidthAndSpacing; topRowWidth += taskWidthAndSpacing;
topSet.add(i); topSet.add(i);
mTopRowIdSet.add(taskId); mTopRowIdSet.add(taskId);
@@ -1926,11 +1951,10 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
widthOffset += getTaskViewAt(j).getLayoutParams().width + mPageSpacing; widthOffset += getTaskViewAt(j).getLayoutParams().width + mPageSpacing;
} }
float gridTranslationX = mIsRtl ? widthOffset : -widthOffset; float currentTaskTranslationX = mIsRtl ? widthOffset : -widthOffset;
gridTranslations[i] += gridTranslationX; gridTranslations[i] += topAccumulatedTranslationX + currentTaskTranslationX;
topAccumulatedTranslationX += gridTranslationX; topAccumulatedTranslationX += currentTaskTranslationX;
} else { } else {
gridTranslations[i] += bottomAccumulatedTranslationX;
bottomRowWidth += taskWidthAndSpacing; bottomRowWidth += taskWidthAndSpacing;
bottomSet.add(i); bottomSet.add(i);
@@ -1946,9 +1970,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
widthOffset += getTaskViewAt(j).getLayoutParams().width + mPageSpacing; widthOffset += getTaskViewAt(j).getLayoutParams().width + mPageSpacing;
} }
float gridTranslationX = mIsRtl ? widthOffset : -widthOffset; float currentTaskTranslationX = mIsRtl ? widthOffset : -widthOffset;
gridTranslations[i] += gridTranslationX; gridTranslations[i] += bottomAccumulatedTranslationX + currentTaskTranslationX;
bottomAccumulatedTranslationX += gridTranslationX; bottomAccumulatedTranslationX += currentTaskTranslationX;
} }
if (taskView == snappedTaskView) { if (taskView == snappedTaskView) {
snappedTaskRowWidth = isTopRow ? topRowWidth : bottomRowWidth; snappedTaskRowWidth = isTopRow ? topRowWidth : bottomRowWidth;
@@ -2139,18 +2163,43 @@ 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 // 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 // alpha is set to 0 so that it can be recycled in the view pool properly
anim.setFloat(taskView, VIEW_ALPHA, 0, ACCEL_2); anim.setFloat(taskView, VIEW_ALPHA, 0, ACCEL_2);
FloatProperty<TaskView> secondaryViewTranslate = SplitSelectStateController splitController = mSplitPlaceholderView.getSplitController();
taskView.getSecondaryDissmissTranslationProperty();
int secondaryTaskDimension = mOrientationHandler.getSecondaryDimension(taskView);
int verticalFactor = mOrientationHandler.getSecondaryTranslationDirectionFactor();
ResourceProvider rp = DynamicResource.provider(mActivity); ResourceProvider rp = DynamicResource.provider(mActivity);
SpringProperty sp = new SpringProperty(SpringProperty.FLAG_CAN_SPRING_ON_START) SpringProperty sp = new SpringProperty(SpringProperty.FLAG_CAN_SPRING_ON_START)
.setDampingRatio(rp.getFloat(R.dimen.dismiss_task_trans_y_damping_ratio)) .setDampingRatio(rp.getFloat(R.dimen.dismiss_task_trans_y_damping_ratio))
.setStiffness(rp.getFloat(R.dimen.dismiss_task_trans_y_stiffness)); .setStiffness(rp.getFloat(R.dimen.dismiss_task_trans_y_stiffness));
FloatProperty<TaskView> dismissingTaskViewTranslate =
taskView.getSecondaryDissmissTranslationProperty();;
// TODO(b/186800707) translate entire grid size distance
int translateDistance = mOrientationHandler.getSecondaryDimension(taskView);
int positiveNegativeFactor = mOrientationHandler.getSecondaryTranslationDirectionFactor();
if (splitController.isSplitSelectActive()) {
// Have the task translate towards whatever side was just pinned
int dir = mOrientationHandler.getSplitTaskViewDismissDirection(splitController
.getActiveSplitPositionOption(), mActivity.getDeviceProfile());
switch (dir) {
case PagedOrientationHandler.SPLIT_TRANSLATE_SECONDARY_NEGATIVE:
dismissingTaskViewTranslate = taskView
.getSecondaryDissmissTranslationProperty();
positiveNegativeFactor = -1;
break;
anim.add(ObjectAnimator.ofFloat(taskView, secondaryViewTranslate, case PagedOrientationHandler.SPLIT_TRANSLATE_PRIMARY_POSITIVE:
verticalFactor * secondaryTaskDimension).setDuration(duration), LINEAR, sp); dismissingTaskViewTranslate = taskView.getPrimaryDismissTranslationProperty();
positiveNegativeFactor = 1;
break;
case PagedOrientationHandler.SPLIT_TRANSLATE_PRIMARY_NEGATIVE:
dismissingTaskViewTranslate = taskView.getPrimaryDismissTranslationProperty();
positiveNegativeFactor = -1;
break;
default:
throw new IllegalStateException("Invalid split task translation: " + dir);
}
}
anim.add(ObjectAnimator.ofFloat(taskView, dismissingTaskViewTranslate,
positiveNegativeFactor * translateDistance).setDuration(duration), LINEAR, sp);
if (LIVE_TILE.get() && taskView.isRunningTask()) { if (LIVE_TILE.get() && taskView.isRunningTask()) {
anim.addOnFrameCallback(() -> { anim.addOnFrameCallback(() -> {
@@ -2215,18 +2264,6 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
} }
} }
// Additional offset for fake landscape, if the pinning happens to the right or
// left, we need to scroll all the tasks away from the direction of the splaceholder
// view
if (isSplitSelectionActive()) {
int splitPosition = getSplitPlaceholder().getSplitController()
.getActiveSplitPositionOption().mStagePosition;
int direction = mOrientationHandler
.getSplitTranslationDirectionFactor(splitPosition);
int splitOffset = mOrientationHandler.getSplitAnimationTranslation(
mSplitPlaceholderView.getHeight(), mActivity.getDeviceProfile());
offset += direction * splitOffset;
}
int scrollDiff = newScroll[i] - oldScroll[i] + offset; int scrollDiff = newScroll[i] - oldScroll[i] + offset;
if (scrollDiff != 0) { if (scrollDiff != 0) {
FloatProperty translationProperty = child instanceof TaskView FloatProperty translationProperty = child instanceof TaskView
@@ -2769,13 +2806,20 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
mLiveTileTaskViewSimulator.recentsViewSecondaryTranslation.value = translation; mLiveTileTaskViewSimulator.recentsViewSecondaryTranslation.value = translation;
} }
protected void setTaskViewsPrimaryTranslation(float translation) { protected void setTaskViewsPrimarySplitTranslation(float translation) {
mTaskViewsPrimaryTranslation = translation; mTaskViewsPrimarySplitTranslation = translation;
for (int i = 0; i < getTaskViewCount(); i++) { for (int i = 0; i < getTaskViewCount(); i++) {
TaskView task = getTaskViewAt(i); TaskView task = getTaskViewAt(i);
task.getPrimaryDismissTranslationProperty().set(task, translation / getScaleY()); task.getPrimarySplitTranslationProperty().set(task, translation);
}
}
protected void setTaskViewsSecondarySplitTranslation(float translation) {
mTaskViewsSecondarySplitTranslation = translation;
for (int i = 0; i < getTaskViewCount(); i++) {
TaskView task = getTaskViewAt(i);
task.getSecondarySplitTranslationProperty().set(task, translation);
} }
mLiveTileTaskViewSimulator.recentsViewPrimaryTranslation.value = translation;
} }
/** /**
@@ -2798,8 +2842,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
public void initiateSplitSelect(TaskView taskView, SplitPositionOption splitPositionOption) { public void initiateSplitSelect(TaskView taskView, SplitPositionOption splitPositionOption) {
mSplitHiddenTaskView = taskView; mSplitHiddenTaskView = taskView;
SplitSelectStateController splitController = mSplitPlaceholderView.getSplitController(); SplitSelectStateController splitController = mSplitPlaceholderView.getSplitController();
splitController.setInitialTaskSelect(taskView, Rect initialBounds = new Rect(taskView.getLeft(), taskView.getTop(), taskView.getRight(),
splitPositionOption); taskView.getBottom());
splitController.setInitialTaskSelect(taskView, splitPositionOption, initialBounds);
mSplitHiddenTaskViewIndex = indexOfChild(taskView); mSplitHiddenTaskViewIndex = indexOfChild(taskView);
mSplitPlaceholderView.setLayoutParams( mSplitPlaceholderView.setLayoutParams(
splitController.getLayoutParamsForActivePosition(getResources(), splitController.getLayoutParamsForActivePosition(getResources(),
@@ -2819,7 +2864,10 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
} }
public PendingAnimation cancelSplitSelect(boolean animate) { public PendingAnimation cancelSplitSelect(boolean animate) {
mSplitPlaceholderView.getSplitController().resetState(); SplitSelectStateController splitController = mSplitPlaceholderView.getSplitController();
SplitPositionOption splitOption = splitController.getActiveSplitPositionOption();
Rect initialBounds = splitController.getInitialBounds();
splitController.resetState();
int duration = mActivity.getStateManager().getState().getTransitionDuration(getContext()); int duration = mActivity.getStateManager().getState().getTransitionDuration(getContext());
PendingAnimation pendingAnim = new PendingAnimation(duration); PendingAnimation pendingAnim = new PendingAnimation(duration);
if (!animate) { if (!animate) {
@@ -2834,8 +2882,6 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
getPageScrolls(oldScroll, false, getPageScrolls(oldScroll, false,
view -> view.getVisibility() != GONE && view != mSplitHiddenTaskView); view -> view.getVisibility() != GONE && view != mSplitHiddenTaskView);
// x is correct, y is before tasks move up
int[] locationOnScreen = mSplitHiddenTaskView.getLocationOnScreen();
int[] newScroll = new int[getChildCount()]; int[] newScroll = new int[getChildCount()];
getPageScrolls(newScroll, false, SIMPLE_SCROLL_LOGIC); getPageScrolls(newScroll, false, SIMPLE_SCROLL_LOGIC);
@@ -2843,20 +2889,42 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
for (int i = mSplitHiddenTaskViewIndex; i >= 0; i--) { for (int i = mSplitHiddenTaskViewIndex; i >= 0; i--) {
View child = getChildAt(i); View child = getChildAt(i);
if (child == mSplitHiddenTaskView) { if (child == mSplitHiddenTaskView) {
TaskView taskView = (TaskView) child;
int left = newScroll[i] + getPaddingStart(); int dir = mOrientationHandler.getSplitTaskViewDismissDirection(splitOption,
int topMargin = mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx; mActivity.getDeviceProfile());
int top = -mSplitHiddenTaskView.getHeight() - locationOnScreen[1]; FloatProperty<TaskView> dismissingTaskViewTranslate;
mSplitHiddenTaskView.layout(left, top, Rect hiddenBounds = new Rect(taskView.getLeft(), taskView.getTop(),
left + mSplitHiddenTaskView.getWidth(), taskView.getRight(), taskView.getBottom());
top + mSplitHiddenTaskView.getHeight()); int distanceDelta = 0;
pendingAnim.add(ObjectAnimator.ofFloat(mSplitHiddenTaskView, TRANSLATION_Y, if (dir == PagedOrientationHandler.SPLIT_TRANSLATE_SECONDARY_NEGATIVE) {
-top + mSplitPlaceholderView.getHeight() - topMargin)); dismissingTaskViewTranslate = taskView
.getSecondaryDissmissTranslationProperty();
distanceDelta = initialBounds.top - hiddenBounds.top;
taskView.layout(initialBounds.left, hiddenBounds.top, initialBounds.right,
hiddenBounds.bottom);
} else {
dismissingTaskViewTranslate = taskView
.getPrimaryDismissTranslationProperty();
distanceDelta = initialBounds.left - hiddenBounds.left;
taskView.layout(hiddenBounds.left, initialBounds.top, hiddenBounds.right,
initialBounds.bottom);
if (dir == PagedOrientationHandler.SPLIT_TRANSLATE_PRIMARY_POSITIVE) {
distanceDelta *= -1;
}
}
pendingAnim.add(ObjectAnimator.ofFloat(mSplitHiddenTaskView,
dismissingTaskViewTranslate,
distanceDelta));
pendingAnim.add(ObjectAnimator.ofFloat(mSplitHiddenTaskView, ALPHA, 1)); pendingAnim.add(ObjectAnimator.ofFloat(mSplitHiddenTaskView, ALPHA, 1));
} else { } else {
// If insertion is on last index (furthest from clear all), we directly add the view // If insertion is on last index (furthest from clear all), we directly add the view
// else we translate all views to the right of insertion index further right, // else we translate all views to the right of insertion index further right,
// ignore views to left // ignore views to left
if (showAsGrid()) {
// TODO(b/186800707) handle more elegantly for grid
continue;
}
int scrollDiff = newScroll[i] - oldScroll[i]; int scrollDiff = newScroll[i] - oldScroll[i];
if (scrollDiff != 0) { if (scrollDiff != 0) {
FloatProperty translationProperty = child instanceof TaskView FloatProperty translationProperty = child instanceof TaskView
@@ -2882,6 +2950,12 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
pendingAnim.addListener(new AnimationSuccessListener() { pendingAnim.addListener(new AnimationSuccessListener() {
@Override @Override
public void onAnimationSuccess(Animator animator) { public void onAnimationSuccess(Animator animator) {
// TODO(b/186800707) Figure out how to undo for grid view
// Need to handle cases where dismissed task is
// * Top Row
// * Bottom Row
// * Focused Task
updateGridProperties();
resetFromSplitSelectionState(); resetFromSplitSelectionState();
} }
}); });
@@ -2891,13 +2965,16 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
private void resetFromSplitSelectionState() { private void resetFromSplitSelectionState() {
mSplitHiddenTaskView.setTranslationY(0); mSplitHiddenTaskView.setTranslationY(0);
int pageToSnapTo = mCurrentPage; if (!showAsGrid()) {
if (mSplitHiddenTaskViewIndex <= pageToSnapTo) { // TODO(b/186800707)
pageToSnapTo += 1; int pageToSnapTo = mCurrentPage;
} else { if (mSplitHiddenTaskViewIndex <= pageToSnapTo) {
pageToSnapTo = mSplitHiddenTaskViewIndex; pageToSnapTo += 1;
} else {
pageToSnapTo = mSplitHiddenTaskViewIndex;
}
snapToPageImmediately(pageToSnapTo);
} }
snapToPageImmediately(pageToSnapTo);
onLayout(false /* changed */, getLeft(), getTop(), getRight(), getBottom()); onLayout(false /* changed */, getLeft(), getTop(), getRight(), getBottom());
resetTaskVisuals(); resetTaskVisuals();
mSplitHiddenTaskView = null; mSplitHiddenTaskView = null;
@@ -172,6 +172,32 @@ public class TaskView extends FrameLayout implements Reusable {
} }
}; };
private static final FloatProperty<TaskView> SPLIT_SELECT_TRANSLATION_X =
new FloatProperty<TaskView>("splitSelectTranslationX") {
@Override
public void setValue(TaskView taskView, float v) {
taskView.setSplitSelectTranslationX(v);
}
@Override
public Float get(TaskView taskView) {
return taskView.mSplitSelectTranslationX;
}
};
private static final FloatProperty<TaskView> SPLIT_SELECT_TRANSLATION_Y =
new FloatProperty<TaskView>("splitSelectTranslationY") {
@Override
public void setValue(TaskView taskView, float v) {
taskView.setSplitSelectTranslationY(v);
}
@Override
public Float get(TaskView taskView) {
return taskView.mSplitSelectTranslationY;
}
};
private static final FloatProperty<TaskView> DISMISS_TRANSLATION_X = private static final FloatProperty<TaskView> DISMISS_TRANSLATION_X =
new FloatProperty<TaskView>("dismissTranslationX") { new FloatProperty<TaskView>("dismissTranslationX") {
@Override @Override
@@ -345,6 +371,9 @@ public class TaskView extends FrameLayout implements Reusable {
// The following grid translations scales with mGridProgress. // The following grid translations scales with mGridProgress.
private float mGridTranslationX; private float mGridTranslationX;
private float mGridTranslationY; private float mGridTranslationY;
// Used when in SplitScreenSelectState
private float mSplitSelectTranslationY;
private float mSplitSelectTranslationX;
private ObjectAnimator mIconAndDimAnimator; private ObjectAnimator mIconAndDimAnimator;
private float mIconScaleAnimStartProgress = 0; private float mIconScaleAnimStartProgress = 0;
@@ -826,8 +855,10 @@ public class TaskView extends FrameLayout implements Reusable {
protected void resetViewTransforms() { protected void resetViewTransforms() {
// fullscreenTranslation and accumulatedTranslation should not be reset, as // fullscreenTranslation and accumulatedTranslation should not be reset, as
// resetViewTransforms is called during Quickswitch scrolling. // resetViewTransforms is called during Quickswitch scrolling.
mDismissTranslationX = mTaskOffsetTranslationX = mTaskResistanceTranslationX = 0f; mDismissTranslationX = mTaskOffsetTranslationX = mTaskResistanceTranslationX =
mDismissTranslationY = mTaskOffsetTranslationY = mTaskResistanceTranslationY = 0f; mSplitSelectTranslationX = 0f;
mDismissTranslationY = mTaskOffsetTranslationY = mTaskResistanceTranslationY =
mSplitSelectTranslationY = 0f;
applyTranslationX(); applyTranslationX();
applyTranslationY(); applyTranslationY();
setTranslationZ(0); setTranslationZ(0);
@@ -957,6 +988,15 @@ public class TaskView extends FrameLayout implements Reusable {
setScaleY(scale); setScaleY(scale);
} }
private void setSplitSelectTranslationX(float x) {
mSplitSelectTranslationX = x;
applyTranslationX();
}
private void setSplitSelectTranslationY(float y) {
mSplitSelectTranslationY = y;
applyTranslationY();
}
private void setDismissTranslationX(float x) { private void setDismissTranslationX(float x) {
mDismissTranslationX = x; mDismissTranslationX = x;
applyTranslationX(); applyTranslationX();
@@ -1057,12 +1097,12 @@ public class TaskView extends FrameLayout implements Reusable {
private void applyTranslationX() { private void applyTranslationX() {
setTranslationX(mDismissTranslationX + mTaskOffsetTranslationX + mTaskResistanceTranslationX setTranslationX(mDismissTranslationX + mTaskOffsetTranslationX + mTaskResistanceTranslationX
+ getPersistentTranslationX()); + mSplitSelectTranslationX + getPersistentTranslationX());
} }
private void applyTranslationY() { private void applyTranslationY() {
setTranslationY(mDismissTranslationY + mTaskOffsetTranslationY + mTaskResistanceTranslationY setTranslationY(mDismissTranslationY + mTaskOffsetTranslationY + mTaskResistanceTranslationY
+ getPersistentTranslationY()); + mSplitSelectTranslationY + getPersistentTranslationY());
} }
/** /**
@@ -1086,6 +1126,16 @@ public class TaskView extends FrameLayout implements Reusable {
+ getGridTrans(mGridTranslationY); + getGridTrans(mGridTranslationY);
} }
public FloatProperty<TaskView> getPrimarySplitTranslationProperty() {
return getPagedOrientationHandler().getPrimaryValue(
SPLIT_SELECT_TRANSLATION_X, SPLIT_SELECT_TRANSLATION_Y);
}
public FloatProperty<TaskView> getSecondarySplitTranslationProperty() {
return getPagedOrientationHandler().getSecondaryValue(
SPLIT_SELECT_TRANSLATION_X, SPLIT_SELECT_TRANSLATION_Y);
}
public FloatProperty<TaskView> getPrimaryDismissTranslationProperty() { public FloatProperty<TaskView> getPrimaryDismissTranslationProperty() {
return getPagedOrientationHandler().getPrimaryValue( return getPagedOrientationHandler().getPrimaryValue(
DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y); DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y);
@@ -159,6 +159,19 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler {
return VIEW_TRANSLATE_X; return VIEW_TRANSLATE_X;
} }
@Override
public int getSplitTaskViewDismissDirection(SplitPositionOption splitPosition,
DeviceProfile dp) {
// Don't use device profile here because we know we're in fake landscape, only split option
// available is top/left
if (splitPosition.mStagePosition == STAGE_POSITION_TOP_OR_LEFT) {
// Top (visually left) side
return SPLIT_TRANSLATE_PRIMARY_NEGATIVE;
}
throw new IllegalStateException("Invalid split stage position: " +
splitPosition.mStagePosition);
}
@Override @Override
public int getPrimaryScroll(View view) { public int getPrimaryScroll(View view) {
return view.getScrollY(); return view.getScrollY();
@@ -42,6 +42,10 @@ import java.util.List;
*/ */
public interface PagedOrientationHandler { public interface PagedOrientationHandler {
int SPLIT_TRANSLATE_PRIMARY_POSITIVE = 0;
int SPLIT_TRANSLATE_PRIMARY_NEGATIVE = 1;
int SPLIT_TRANSLATE_SECONDARY_NEGATIVE = 2;
PagedOrientationHandler PORTRAIT = new PortraitPagedViewHandler(); PagedOrientationHandler PORTRAIT = new PortraitPagedViewHandler();
PagedOrientationHandler LANDSCAPE = new LandscapePagedViewHandler(); PagedOrientationHandler LANDSCAPE = new LandscapePagedViewHandler();
PagedOrientationHandler SEASCAPE = new SeascapePagedViewHandler(); PagedOrientationHandler SEASCAPE = new SeascapePagedViewHandler();
@@ -71,6 +75,13 @@ public interface PagedOrientationHandler {
int getSecondaryDimension(View view); int getSecondaryDimension(View view);
FloatProperty<View> getPrimaryViewTranslate(); FloatProperty<View> getPrimaryViewTranslate();
FloatProperty<View> getSecondaryViewTranslate(); FloatProperty<View> getSecondaryViewTranslate();
/**
* @param splitPosition The position where the view to be split will go
* @return {@link #SPLIT_TRANSLATE_*} constants to indicate which direction the
* dismissal should happen
*/
int getSplitTaskViewDismissDirection(SplitPositionOption splitPosition, DeviceProfile dp);
int getPrimaryScroll(View view); int getPrimaryScroll(View view);
float getPrimaryScale(View view); float getPrimaryScale(View view);
int getChildStart(View view); int getChildStart(View view);
@@ -155,6 +155,25 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler {
return VIEW_TRANSLATE_Y; return VIEW_TRANSLATE_Y;
} }
@Override
public int getSplitTaskViewDismissDirection(SplitPositionOption splitPosition,
DeviceProfile dp) {
if (splitPosition.mStagePosition == STAGE_POSITION_TOP_OR_LEFT) {
if (dp.isLandscape) {
// Left side
return SPLIT_TRANSLATE_PRIMARY_NEGATIVE;
} else {
// Top side
return SPLIT_TRANSLATE_SECONDARY_NEGATIVE;
}
} else if (splitPosition.mStagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
// We don't have a bottom option, so should be right
return SPLIT_TRANSLATE_PRIMARY_POSITIVE;
}
throw new IllegalStateException("Invalid split stage position: " +
splitPosition.mStagePosition);
}
@Override @Override
public int getPrimaryScroll(View view) { public int getPrimaryScroll(View view) {
return view.getScrollX(); return view.getScrollX();