Merge "Update KQS overview launch animation" into main

This commit is contained in:
Schneider Victor-tulias
2024-02-01 17:55:54 +00:00
committed by Android (Google) Code Review
13 changed files with 289 additions and 82 deletions
@@ -22,6 +22,7 @@ import android.content.pm.ActivityInfo;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.R;
import com.android.launcher3.statehandlers.DesktopVisibilityController;
@@ -47,7 +48,8 @@ import java.util.stream.Collectors;
public final class KeyboardQuickSwitchController implements
TaskbarControllers.LoggableTaskbarController {
static final int MAX_TASKS = 6;
@VisibleForTesting
public static final int MAX_TASKS = 6;
@NonNull private final ControllerCallbacks mControllerCallbacks = new ControllerCallbacks();
@@ -22,7 +22,9 @@ import static android.view.Surface.ROTATION_90;
import static android.widget.Toast.LENGTH_SHORT;
import static com.android.app.animation.Interpolators.ACCELERATE_DECELERATE;
import static com.android.app.animation.Interpolators.EMPHASIZED;
import static com.android.app.animation.Interpolators.DECELERATE;
import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.app.animation.Interpolators.OVERSHOOT_1_2;
import static com.android.launcher3.BaseActivity.EVENT_DESTROYED;
import static com.android.launcher3.BaseActivity.EVENT_STARTED;
@@ -134,6 +136,7 @@ import com.android.quickstep.util.SurfaceTransaction;
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;
import com.android.quickstep.views.TaskView.TaskIdAttributeContainer;
@@ -167,6 +170,9 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
private static final ArrayList<String> STATE_NAMES = new ArrayList<>();
// Fraction of the scroll and transform animation in which the current task fades out
private static final float KQS_TASK_FADE_ANIMATION_FRACTION = 0.4f;
protected final BaseActivityInterface<S, T> mActivityInterface;
protected final InputConsumerProxy mInputConsumerProxy;
protected final ActivityInitListener mActivityInitListener;
@@ -900,7 +906,10 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
return;
}
mLauncherTransitionController.setProgress(
Math.max(mCurrentShift.value, getScaleProgressDueToScroll()), mDragLengthFactor);
// Immediately finish the grid transition
isKeyboardTaskFocusPending()
? 1f : Math.max(mCurrentShift.value, getScaleProgressDueToScroll()),
mDragLengthFactor);
}
/**
@@ -1349,7 +1358,9 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
}
Interpolator interpolator;
S state = mActivityInterface.stateFromGestureEndTarget(endTarget);
if (state.displayOverviewTasksAsGrid(mDp)) {
if (isKeyboardTaskFocusPending()) {
interpolator = EMPHASIZED;
} else if (state.displayOverviewTasksAsGrid(mDp)) {
interpolator = ACCELERATE_DECELERATE;
} else if (endTarget == RECENTS) {
interpolator = OVERSHOOT_1_2;
@@ -1653,7 +1664,8 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
animatorSet.play(windowAnim);
if (mRecentsView != null) {
mRecentsView.onPrepareGestureEndAnimation(
animatorSet, mGestureState.getEndTarget(),
mGestureState.isHandlingAtomicEvent() ? null : animatorSet,
mGestureState.getEndTarget(),
getRemoteTaskViewSimulators());
}
animatorSet.setDuration(duration).setInterpolator(interpolator);
@@ -2225,6 +2237,14 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
}
}
private boolean shouldLinkRecentsViewScroll() {
return mRecentsViewScrollLinked && !isKeyboardTaskFocusPending();
}
private boolean isKeyboardTaskFocusPending() {
return mRecentsView != null && mRecentsView.isKeyboardTaskFocusPending();
}
private void onRecentsViewScroll() {
if (moveWindowWithRecentsScroll()) {
onCurrentShiftUpdated();
@@ -2457,6 +2477,44 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
mActivityInitListener.register();
}
private boolean shouldFadeOutTargetsForKeyboardQuickSwitch(
TransformParams transformParams,
TaskViewSimulator taskViewSimulator,
float progress) {
RemoteAnimationTargets targets = transformParams.getTargetSet();
boolean fadeAppTargets = isKeyboardTaskFocusPending()
&& targets != null
&& targets.apps != null
&& targets.apps.length > 0;
float fadeProgress = Utilities.mapBoundToRange(
progress,
/* lowerBound= */ 0f,
/* upperBound= */ KQS_TASK_FADE_ANIMATION_FRACTION,
/* toMin= */ 0f,
/* toMax= */ 1f,
LINEAR);
if (!fadeAppTargets || Float.compare(fadeProgress, 1f) == 0) {
return false;
}
SurfaceTransaction surfaceTransaction =
transformParams.createSurfaceParams(taskViewSimulator);
SurfaceControl.Transaction transaction = surfaceTransaction.getTransaction();
for (RemoteAnimationTarget app : targets.apps) {
transaction.setAlpha(app.leash, 1f - fadeProgress);
transaction.setPosition(app.leash,
/* x= */ app.startBounds.left
+ (mActivity.getDeviceProfile().overviewPageSpacing
* (mRecentsView.isRtl() ? fadeProgress : -fadeProgress)),
/* y= */ 0f);
transaction.setScale(app.leash, 1f, 1f);
taskViewSimulator.taskPrimaryTranslation.value =
mRecentsView.getScrollOffsetForKeyboardTaskFocus();
taskViewSimulator.apply(transformParams, surfaceTransaction);
}
return true;
}
/**
* Applies the transform on the recents animation
*/
@@ -2466,7 +2524,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
// swipe-to-icon animation is handled by RectFSpringAnim anim
boolean notSwipingToHome = mRecentsAnimationTargets != null
&& mGestureState.getEndTarget() != HOME;
boolean setRecentsScroll = mRecentsViewScrollLinked && mRecentsView != null;
boolean setRecentsScroll = shouldLinkRecentsViewScroll() && mRecentsView != null;
float progress = Math.max(mCurrentShift.value, getScaleProgressDueToScroll());
int scrollOffset = setRecentsScroll ? mRecentsView.getScrollOffset() : 0;
if (!mStartMovingTasks && (progress > 0 || scrollOffset != 0)) {
@@ -2485,7 +2543,12 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
if (setRecentsScroll) {
taskViewSimulator.setScroll(scrollOffset);
}
taskViewSimulator.apply(remoteHandle.getTransformParams());
TransformParams transformParams = remoteHandle.getTransformParams();
if (shouldFadeOutTargetsForKeyboardQuickSwitch(
transformParams, taskViewSimulator, progress)) {
continue;
}
taskViewSimulator.apply(transformParams);
}
}
}
@@ -2493,7 +2556,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
// Scaling of RecentsView during quick switch based on amount of recents scroll
private float getScaleProgressDueToScroll() {
if (mActivity == null || !mActivity.getDeviceProfile().isTablet || mRecentsView == null
|| !mRecentsViewScrollLinked) {
|| !shouldLinkRecentsViewScroll()) {
return 0;
}
@@ -178,7 +178,7 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T
public abstract <T extends RecentsView> T getVisibleRecentsView();
@UiThread
public abstract boolean switchToRecentsIfVisible(Runnable onCompleteCallback);
public abstract boolean switchToRecentsIfVisible(Animator.AnimatorListener animatorListener);
public abstract Rect getOverviewWindowBounds(
Rect homeBounds, RemoteAnimationTarget target);
@@ -520,7 +520,7 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T
// Since we are changing the start position of the UI, reapply the state, at the end
controller.setEndAction(() -> mActivity.getStateManager().goToState(
controller.getInterpolatedProgress() > 0.5 ? mTargetState : mBackgroundState,
false));
/* animated= */ false));
RecentsView recentsView = mActivity.getOverviewPanel();
AnimatorControllerWithResistance controllerWithResistance =
@@ -122,7 +122,7 @@ public final class FallbackActivityInterface extends
}
@Override
public boolean switchToRecentsIfVisible(Runnable onCompleteCallback) {
public boolean switchToRecentsIfVisible(Animator.AnimatorListener animatorListener) {
return false;
}
@@ -21,7 +21,6 @@ import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.FLOATING_SEARCH_BAR;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
@@ -212,7 +211,7 @@ public final class LauncherActivityInterface extends
}
@Override
public boolean switchToRecentsIfVisible(Runnable onCompleteCallback) {
public boolean switchToRecentsIfVisible(Animator.AnimatorListener animatorListener) {
Launcher launcher = getVisibleLauncher();
if (launcher == null) {
return false;
@@ -227,7 +226,7 @@ public final class LauncherActivityInterface extends
closeOverlay();
launcher.getStateManager().goToState(OVERVIEW,
launcher.getStateManager().shouldAnimateStateChange(),
onCompleteCallback == null ? null : forEndCallback(onCompleteCallback));
animatorListener);
return true;
}
@@ -15,9 +15,12 @@
*/
package com.android.quickstep;
import static com.android.launcher3.PagedView.INVALID_PAGE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Intent;
import android.graphics.PointF;
import android.os.SystemClock;
@@ -25,7 +28,6 @@ import android.os.Trace;
import android.view.View;
import androidx.annotation.BinderThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
@@ -75,7 +77,7 @@ public class OverviewCommandHelper {
* do not lose the focus across multiple calls of
* {@link OverviewCommandHelper#executeCommand(CommandInfo)} for the same command
*/
private int mTaskFocusIndexOverride = -1;
private int mKeyboardTaskFocusIndex = -1;
/**
* Whether we should incoming toggle commands while a previous toggle command is still ongoing.
@@ -195,9 +197,11 @@ public class OverviewCommandHelper {
}
BaseActivityInterface<?, T> activityInterface =
mOverviewComponentObserver.getActivityInterface();
RecentsView recents = activityInterface.getVisibleRecentsView();
if (recents == null) {
RecentsView visibleRecentsView = activityInterface.getVisibleRecentsView();
RecentsView createdRecentsView;
if (visibleRecentsView == null) {
T activity = activityInterface.getCreatedActivity();
createdRecentsView = activity == null ? null : activity.getOverviewPanel();
DeviceProfile dp = activity == null ? null : activity.getDeviceProfile();
TaskbarUIController uiController = activityInterface.getTaskbarController();
boolean allowQuickSwitch = FeatureFlags.ENABLE_KEYBOARD_QUICK_SWITCH.get()
@@ -209,8 +213,8 @@ public class OverviewCommandHelper {
if (!allowQuickSwitch) {
return true;
}
mTaskFocusIndexOverride = uiController.launchFocusedTask();
if (mTaskFocusIndexOverride == -1) {
mKeyboardTaskFocusIndex = uiController.launchFocusedTask();
if (mKeyboardTaskFocusIndex == -1) {
return true;
}
}
@@ -224,34 +228,47 @@ public class OverviewCommandHelper {
return true;
}
} else {
createdRecentsView = visibleRecentsView;
switch (cmd.type) {
case TYPE_SHOW:
// already visible
return true;
case TYPE_HIDE: {
mTaskFocusIndexOverride = -1;
int currentPage = recents.getNextPage();
TaskView tv = (currentPage >= 0 && currentPage < recents.getTaskViewCount())
? (TaskView) recents.getPageAt(currentPage)
mKeyboardTaskFocusIndex = INVALID_PAGE;
int currentPage = visibleRecentsView.getNextPage();
TaskView tv = (currentPage >= 0
&& currentPage < visibleRecentsView.getTaskViewCount())
? (TaskView) visibleRecentsView.getPageAt(currentPage)
: null;
return launchTask(recents, tv, cmd);
return launchTask(visibleRecentsView, tv, cmd);
}
case TYPE_TOGGLE:
return launchTask(recents, getNextTask(recents), cmd);
return launchTask(visibleRecentsView, getNextTask(visibleRecentsView), cmd);
case TYPE_HOME:
recents.startHome();
visibleRecentsView.startHome();
return true;
}
}
final Runnable completeCallback = () -> {
RecentsView rv = activityInterface.getVisibleRecentsView();
if (rv != null && (cmd.type == TYPE_KEYBOARD_INPUT || cmd.type == TYPE_HIDE)) {
updateRecentsViewFocus(rv);
if (createdRecentsView != null) {
createdRecentsView.setKeyboardTaskFocusIndex(mKeyboardTaskFocusIndex);
}
// Handle recents view focus when launching from home
Animator.AnimatorListener animatorListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
updateRecentsViewFocus(cmd);
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
onRecentsViewFocusUpdated(cmd);
scheduleNextTask(cmd);
}
scheduleNextTask(cmd);
};
if (activityInterface.switchToRecentsIfVisible(completeCallback)) {
if (activityInterface.switchToRecentsIfVisible(animatorListener)) {
// If successfully switched, wait until animation finishes
return false;
}
@@ -276,6 +293,7 @@ public class OverviewCommandHelper {
@Override
public void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targets) {
updateRecentsViewFocus(cmd);
activityInterface.runOnInitBackgroundStateUI(() ->
interactionHandler.onGestureEnded(0, new PointF()));
cmd.removeListener(this);
@@ -290,14 +308,12 @@ public class OverviewCommandHelper {
if (createdActivity == null) {
return;
}
RecentsView createdRecents = createdActivity.getOverviewPanel();
if (createdRecents != null) {
createdRecents.onRecentsAnimationComplete();
if (createdRecentsView != null) {
createdRecentsView.onRecentsAnimationComplete();
}
}
};
RecentsView<?, ?> visibleRecentsView = activityInterface.getVisibleRecentsView();
if (visibleRecentsView != null) {
visibleRecentsView.moveRunningTaskToFront();
}
@@ -317,7 +333,6 @@ public class OverviewCommandHelper {
interactionHandler.onGestureStarted(false /*isLikelyToStartNewTask*/);
cmd.mActiveCallbacks.addListener(recentAnimListener);
}
Trace.beginAsyncSection(TRANSITION_NAME, 0);
return false;
}
@@ -325,47 +340,58 @@ public class OverviewCommandHelper {
private void onTransitionComplete(CommandInfo cmd, AbsSwipeUpHandler handler) {
cmd.removeListener(handler);
Trace.endAsyncSection(TRANSITION_NAME, 0);
RecentsView rv =
mOverviewComponentObserver.getActivityInterface().getVisibleRecentsView();
if (rv != null && (cmd.type == TYPE_KEYBOARD_INPUT || cmd.type == TYPE_HIDE)) {
updateRecentsViewFocus(rv);
}
onRecentsViewFocusUpdated(cmd);
scheduleNextTask(cmd);
}
private void updateRecentsViewFocus(@NonNull RecentsView rv) {
private void updateRecentsViewFocus(CommandInfo cmd) {
RecentsView recentsView =
mOverviewComponentObserver.getActivityInterface().getVisibleRecentsView();
if (recentsView == null || (cmd.type != TYPE_KEYBOARD_INPUT && cmd.type != TYPE_HIDE)) {
return;
}
// When the overview is launched via alt tab (cmd type is TYPE_KEYBOARD_INPUT),
// the touch mode somehow is not change to false by the Android framework.
// The subsequent tab to go through tasks in overview can only be dispatched to
// focuses views, while focus can only be requested in
// {@link View#requestFocusNoSearch(int, Rect)} when touch mode is false. To note,
// here we launch overview with live tile.
rv.getViewRootImpl().touchModeChanged(false);
recentsView.getViewRootImpl().touchModeChanged(false);
// Ensure that recents view has focus so that it receives the followup key inputs
TaskView taskView = rv.getTaskViewAt(mTaskFocusIndexOverride);
if (taskView != null) {
requestFocus(taskView);
if (requestFocus(recentsView.getTaskViewAt(mKeyboardTaskFocusIndex))) {
return;
}
taskView = rv.getNextTaskView();
if (taskView != null) {
requestFocus(taskView);
if (requestFocus(recentsView.getNextTaskView())) {
return;
}
taskView = rv.getTaskViewAt(0);
if (taskView != null) {
requestFocus(taskView);
if (requestFocus(recentsView.getTaskViewAt(0))) {
return;
}
requestFocus(rv);
requestFocus(recentsView);
}
private void requestFocus(@NonNull View view) {
view.post(() -> {
view.requestFocus();
view.requestAccessibilityFocus();
private void onRecentsViewFocusUpdated(CommandInfo cmd) {
RecentsView recentsView =
mOverviewComponentObserver.getActivityInterface().getVisibleRecentsView();
if (recentsView == null
|| cmd.type != TYPE_HIDE
|| mKeyboardTaskFocusIndex == INVALID_PAGE) {
return;
}
recentsView.setKeyboardTaskFocusIndex(INVALID_PAGE);
recentsView.setCurrentPage(mKeyboardTaskFocusIndex);
mKeyboardTaskFocusIndex = INVALID_PAGE;
}
private boolean requestFocus(@Nullable View taskView) {
if (taskView == null) {
return false;
}
taskView.post(() -> {
taskView.requestFocus();
taskView.requestAccessibilityFocus();
});
return true;
}
public void dump(PrintWriter pw) {
@@ -374,7 +400,7 @@ public class OverviewCommandHelper {
if (!mPendingCommands.isEmpty()) {
pw.println(" pendingCommandType=" + mPendingCommands.get(0).type);
}
pw.println(" mTaskFocusIndexOverride=" + mTaskFocusIndexOverride);
pw.println(" mKeyboardTaskFocusIndex=" + mKeyboardTaskFocusIndex);
pw.println(" mWaitForToggleCommandComplete=" + mWaitForToggleCommandComplete);
}
@@ -40,6 +40,7 @@ import android.util.Log;
import android.view.RemoteAnimationTarget;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Utilities;
@@ -344,6 +345,14 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
* Applies the target to the previously set parameters
*/
public void apply(TransformParams params) {
apply(params, null);
}
/**
* Applies the target to the previously set parameters, optionally with an overridden
* surface transaction
*/
public void apply(TransformParams params, @Nullable SurfaceTransaction surfaceTransaction) {
if (mDp == null || mThumbnailPosition.isEmpty()) {
return;
}
@@ -404,7 +413,8 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
mTempRectF.roundOut(mTmpCropRect);
params.setProgress(1f - fullScreenProgress);
params.applySurfaceParams(params.createSurfaceParams(this));
params.applySurfaceParams(surfaceTransaction == null
? params.createSurfaceParams(this) : surfaceTransaction);
if (!DEBUG) {
return;
@@ -542,6 +542,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
private int mOverScrollShift = 0;
private long mScrollLastHapticTimestamp;
private int mKeyboardTaskFocusSnapAnimationDuration;
private int mKeyboardTaskFocusIndex = INVALID_PAGE;
/**
* TODO: Call reloadIdNeeded in onTaskStackChanged.
*/
@@ -1704,6 +1707,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
// Removing views sets the currentPage to 0, so we save this and restore it after
// the new set of views are added
int previousCurrentPage = mCurrentPage;
int previousFocusedPage = indexOfChild(getFocusedChild());
removeAllViews();
// If we are entering Overview as a result of initiating a split from somewhere else
@@ -1833,6 +1837,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
targetPage = indexOfChild(currentTaskView);
}
}
} else if (previousFocusedPage != INVALID_PAGE) {
targetPage = previousFocusedPage;
} else {
// Set the current page to the running task, but not if settling on new task.
if (hasAnyValidTaskIds(runningTaskId)) {
@@ -2914,7 +2920,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
int focusedTaskShift = 0;
int focusedTaskWidthAndSpacing = 0;
int snappedTaskRowWidth = 0;
int snappedPage = getNextPage();
int snappedPage = isKeyboardTaskFocusPending() ? mKeyboardTaskFocusIndex : getNextPage();
TaskView snappedTaskView = getTaskViewAt(snappedPage);
TaskView homeTaskView = getHomeTaskView();
TaskView nextFocusedTaskView = null;
@@ -5576,6 +5582,19 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
return getScrollOffset(getRunningTaskIndex());
}
/**
* Returns how many pixels the running task is offset on the currently laid out dominant axis
* specifically during a Keyboard task focus.
*/
public int getScrollOffsetForKeyboardTaskFocus() {
if (!isKeyboardTaskFocusPending()) {
return getScrollOffset(getRunningTaskIndex());
}
return getPagedOrientationHandler().getPrimaryScroll(this)
- getScrollForPage(mKeyboardTaskFocusIndex)
+ getScrollOffset(getRunningTaskIndex());
}
/**
* Sets whether or not we should clamp the scroll offset.
* This is used to avoid x-axis movement when swiping up transient taskbar.
@@ -5609,14 +5628,14 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
if (pageIndex == -1) {
return 0;
}
int overScrollShift = getOverScrollShift();
if (mAdjacentPageHorizontalOffset > 0) {
// Don't dampen the scroll (due to overscroll) if the adjacent tasks are offscreen, so
// that the page can move freely given there's no visual indication why it shouldn't.
overScrollShift = (int) Utilities.mapRange(mAdjacentPageHorizontalOffset,
overScrollShift, getUndampedOverScrollShift());
}
// Don't dampen the scroll (due to overscroll) if the adjacent tasks are offscreen, so that
// the page can move freely given there's no visual indication why it shouldn't.
int overScrollShift = mAdjacentPageHorizontalOffset > 0
? (int) Utilities.mapRange(
mAdjacentPageHorizontalOffset,
getOverScrollShift(),
getUndampedOverScrollShift())
: getOverScrollShift();
return getScrollForPage(pageIndex) - getPagedOrientationHandler().getPrimaryScroll(this)
+ overScrollShift + getOffsetFromScrollPosition(pageIndex);
}
@@ -6025,11 +6044,50 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
dispatchScrollChanged();
}
/**
* Prepares this RecentsView to scroll properly for an upcoming child view focus request from
* keyboard quick switching
*/
public void setKeyboardTaskFocusIndex(int taskIndex) {
mKeyboardTaskFocusIndex = taskIndex;
}
/** Returns whether this RecentsView will be scrolling to a child view for a focus request */
public boolean isKeyboardTaskFocusPending() {
return mKeyboardTaskFocusIndex != INVALID_PAGE;
}
private boolean isKeyboardTaskFocusPendingForChild(View child) {
return isKeyboardTaskFocusPending() && mKeyboardTaskFocusIndex == indexOfChild(child);
}
@Override
protected boolean shouldHandleRequestChildFocus() {
// If we are already scrolling to a task view, then the focus request has already been
// handled
return mScroller.isFinished();
protected int getSnapAnimationDuration() {
return isKeyboardTaskFocusPending()
? mKeyboardTaskFocusSnapAnimationDuration : super.getSnapAnimationDuration();
}
@Override
protected void onVelocityValuesUpdated() {
super.onVelocityValuesUpdated();
mKeyboardTaskFocusSnapAnimationDuration =
getResources().getInteger(R.integer.config_keyboardTaskFocusSnapAnimationDuration);
}
@Override
protected boolean shouldHandleRequestChildFocus(View child) {
// If we are already scrolling to a task view and we aren't focusing to this child from
// keyboard quick switch, then the focus request has already been handled
return mScroller.isFinished() || isKeyboardTaskFocusPendingForChild(child);
}
@Override
public void requestChildFocus(View child, View focused) {
if (isKeyboardTaskFocusPendingForChild(child)) {
updateGridProperties();
updateScrollSynchronously();
}
super.requestChildFocus(child, focused);
}
private void dispatchScrollChanged() {