import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
import com.android.systemui.recents.events.ui.ResizeTaskEvent;
import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
+import com.android.systemui.recents.events.ui.StackViewScrolledEvent;
import com.android.systemui.recents.events.ui.UserInteractionEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
public final static int EVENT_BUS_PRIORITY = Recents.EVENT_BUS_PRIORITY + 1;
- RecentsPackageMonitor mPackageMonitor;
- long mLastTabKeyEventTime;
- boolean mFinishedOnStartup;
+ private RecentsPackageMonitor mPackageMonitor;
+ private long mLastTabKeyEventTime;
+ private boolean mFinishedOnStartup;
+ private boolean mIgnoreAltTabRelease;
// Top level views
- RecentsView mRecentsView;
- SystemBarScrimViews mScrimViews;
- ViewStub mEmptyViewStub;
- View mEmptyView;
+ private RecentsView mRecentsView;
+ private SystemBarScrimViews mScrimViews;
+ private ViewStub mEmptyViewStub;
+ private View mEmptyView;
// Resize task debug
- RecentsResizeTaskDialog mResizeTaskDebugDialog;
+ private RecentsResizeTaskDialog mResizeTaskDebugDialog;
// Search AppWidget
- AppWidgetProviderInfo mSearchWidgetInfo;
- RecentsAppWidgetHost mAppWidgetHost;
- RecentsAppWidgetHostView mSearchWidgetHostView;
+ private AppWidgetProviderInfo mSearchWidgetInfo;
+ private RecentsAppWidgetHost mAppWidgetHost;
+ private RecentsAppWidgetHostView mSearchWidgetHostView;
// Runnables to finish the Recents activity
- FinishRecentsRunnable mFinishLaunchHomeRunnable;
+ private FinishRecentsRunnable mFinishLaunchHomeRunnable;
// The trigger to automatically launch the current task
- DozeTrigger mIterateTrigger = new DozeTrigger(500, new Runnable() {
+ private DozeTrigger mIterateTrigger = new DozeTrigger(500, new Runnable() {
@Override
public void run() {
dismissRecentsToFocusedTask();
protected void onStop() {
super.onStop();
+ // Reset some states
+ mIgnoreAltTabRelease = false;
+
// Notify that recents is now hidden
SystemServicesProxy ssp = Recents.getSystemServices();
EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, false));
boolean hasRepKeyTimeElapsed = (SystemClock.elapsedRealtime() -
mLastTabKeyEventTime) > altTabKeyDelay;
if (event.getRepeatCount() <= 0 || hasRepKeyTimeElapsed) {
- // As we iterate to the next/previous task, cancel any current/lagging window
- // transition animations
- EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
-
// Focus the next task in the stack
final boolean backward = event.isShiftPressed();
if (backward) {
EventBus.getDefault().send(new FocusNextTaskViewEvent());
}
mLastTabKeyEventTime = SystemClock.elapsedRealtime();
+
+ // In the case of another ALT event, don't ignore the next release
+ if (event.isAltPressed()) {
+ mIgnoreAltTabRelease = false;
+ }
}
return true;
}
public final void onBusEvent(HideRecentsEvent event) {
if (event.triggeredFromAltTab) {
// If we are hiding from releasing Alt-Tab, dismiss Recents to the focused app
- dismissRecentsToFocusedTaskOrHome();
+ if (!mIgnoreAltTabRelease) {
+ dismissRecentsToFocusedTaskOrHome();
+ }
} else if (event.triggeredFromHomeKey) {
// Otherwise, dismiss Recents to Home
dismissRecentsToHome(true /* animated */);
finish();
}
+ public final void onBusEvent(StackViewScrolledEvent event) {
+ // Once the user has scrolled while holding alt-tab, then we should ignore the release of
+ // the key
+ mIgnoreAltTabRelease = true;
+ }
+
private void refreshSearchWidgetView() {
if (mSearchWidgetInfo != null) {
SystemServicesProxy ssp = Recents.getSystemServices();
// Update the header bar if necessary
reloadHeaderBarLayout(false /* tryAndBindSearchWidget */);
- if (sInstanceLoadPlan == null) {
- // Create a new load plan if onPreloadRecents() was never triggered
+ // In the case where alt-tab is triggered, we never get a preloadRecents() call, so we
+ // should always preload the tasks now
+ if (mTriggeredFromAltTab ||sInstanceLoadPlan == null) {
+ // Create a new load plan if preloadRecents() was never triggered
sInstanceLoadPlan = loader.createLoadPlan(mContext);
}
-
- if (!sInstanceLoadPlan.hasTasks()) {
+ if (mTriggeredFromAltTab || !sInstanceLoadPlan.hasTasks()) {
loader.preloadTasks(sInstanceLoadPlan, isTopTaskHome);
}
TaskStack stack = sInstanceLoadPlan.getTaskStack();
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
+import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
import com.android.systemui.recents.events.activity.IterateRecentsEvent;
import com.android.systemui.recents.events.activity.PackagesChangedEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
boolean mStackViewsDirty = true;
boolean mStackViewsClipDirty = true;
boolean mAwaitingFirstLayout = true;
+ boolean mEnterAnimationComplete = false;
boolean mStartEnterAnimationRequestedAfterLayout;
ViewAnimation.TaskViewEnterContext mStartEnterAnimationContext;
mStackViewsDirty = true;
mStackViewsClipDirty = true;
mAwaitingFirstLayout = true;
+ mEnterAnimationComplete = false;
if (mUIDozeTrigger != null) {
mUIDozeTrigger.stopDozing();
mUIDozeTrigger.resetTrigger();
/**
* Sets the focused task to the provided (bounded taskIndex).
+ *
+ * @return whether or not the stack will scroll as a part of this focus change
*/
- private void setFocusedTask(int taskIndex, boolean scrollToTask, final boolean animated) {
- setFocusedTask(taskIndex, scrollToTask, animated, true);
+ private boolean setFocusedTask(int taskIndex, boolean scrollToTask, final boolean animated) {
+ return setFocusedTask(taskIndex, scrollToTask, animated, true);
}
/**
* Sets the focused task to the provided (bounded taskIndex).
+ *
+ * @return whether or not the stack will scroll as a part of this focus change
*/
- private void setFocusedTask(int taskIndex, boolean scrollToTask, final boolean animated,
+ private boolean setFocusedTask(int taskIndex, boolean scrollToTask, final boolean animated,
final boolean requestViewFocus) {
// Find the next task to focus
int newFocusedTaskIndex = mStack.getTaskCount() > 0 ?
}
}
+ boolean willScroll = false;
mFocusedTaskIndex = newFocusedTaskIndex;
if (mFocusedTaskIndex != -1) {
Runnable focusTaskRunnable = new Runnable() {
newScroll -= 0.5f;
}
newScroll = mStackScroller.getBoundedStackScroll(newScroll);
- mStackScroller.animateScroll(mStackScroller.getStackScroll(), newScroll,
- focusTaskRunnable);
+ if (Float.compare(newScroll, mStackScroller.getStackScroll()) != 0) {
+ mStackScroller.animateScroll(mStackScroller.getStackScroll(), newScroll,
+ focusTaskRunnable);
+ willScroll = true;
+
+ // Cancel any running enter animations at this point when we scroll as well
+ if (!mEnterAnimationComplete) {
+ final List<TaskView> taskViews = getTaskViews();
+ for (TaskView tv : taskViews) {
+ tv.cancelEnterRecentsAnimation();
+ }
+ }
+ } else {
+ focusTaskRunnable.run();
+ }
mLayoutAlgorithm.animateFocusState(TaskStackLayoutAlgorithm.STATE_FOCUSED);
} else {
focusTaskRunnable.run();
}
}
+ return willScroll;
}
/**
* Sets the focused task relative to the currently focused task.
*
+ * @param forward whether to go to the next task in the stack (along the curve) or the previous
* @param stackTasksOnly if set, will ensure that the traversal only goes along stack tasks, and
* if the currently focused task is not a stack task, will set the focus
* to the first visible stack task
* focus.
*/
public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated) {
+ setRelativeFocusedTask(forward, stackTasksOnly, animated, false);
+ }
+
+ /**
+ * Sets the focused task relative to the currently focused task.
+ *
+ * @param forward whether to go to the next task in the stack (along the curve) or the previous
+ * @param stackTasksOnly if set, will ensure that the traversal only goes along stack tasks, and
+ * if the currently focused task is not a stack task, will set the focus
+ * to the first visible stack task
+ * @param animated determines whether to actually draw the highlight along with the change in
+ * focus.
+ * @param cancelWindowAnimations if set, will attempt to cancel window animations if a scroll
+ * happens
+ */
+ public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated,
+ boolean cancelWindowAnimations) {
int newIndex = -1;
if (mFocusedTaskIndex != -1) {
if (stackTasksOnly) {
}
}
if (newIndex != -1) {
- setFocusedTask(newIndex, true, animated);
+ boolean willScroll = setFocusedTask(newIndex, true, animated);
+ if (willScroll && cancelWindowAnimations) {
+ // As we iterate to the next/previous task, cancel any current/lagging window
+ // transition animations
+ EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
+ }
}
}
mLayoutAlgorithm.animateFocusState(mLayoutAlgorithm.getDefaultFocusState());
}
+ public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
+ mEnterAnimationComplete = true;
+ }
+
/**
* Removes the task from the stack, and updates the focus to the next task in the stack if the
* removed TaskView was focused.
mScrollAnimator.setDuration(mContext.getResources().getInteger(
R.integer.recents_animate_task_stack_scroll_duration));
mScrollAnimator.setInterpolator(mLinearOutSlowInInterpolator);
- mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- setStackScroll((Float) animation.getAnimatedValue());
- }
- });
mScrollAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
package com.android.systemui.recents.views;
import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
-import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import com.android.systemui.R;
-import com.android.systemui.recents.Constants;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsActivity;
import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
animate().alpha(1f)
.translationY(transform.translationY)
.setUpdateListener(null)
- .setInterpolator(mFastOutSlowInInterpolator)
- .setDuration(taskViewEnterFromHomeDuration)
- .withEndAction(new Runnable() {
+ .setListener(new AnimatorListenerAdapter() {
+ private boolean hasEnded;
+
+ // We use the animation listener instead of withEndAction() to
+ // ensure that onAnimationEnd() is called when the animator is
+ // cancelled
@Override
- public void run() {
- // Decrement the post animation trigger
+ public void onAnimationEnd(Animator animation) {
+ if (hasEnded) return;
ctx.postAnimationTrigger.decrement();
+ hasEnded = true;
}
})
+ .setInterpolator(mFastOutSlowInInterpolator)
+ .setDuration(taskViewEnterFromHomeDuration)
.start();
ctx.postAnimationTrigger.increment();
}
.translationY(transform.translationY)
.setStartDelay(delay)
.setUpdateListener(ctx.updateListener)
- .setInterpolator(mQuintOutInterpolator)
- .setDuration(taskViewEnterFromHomeDuration +
- frontIndex * taskViewEnterFromHomeStaggerDelay)
- .withEndAction(new Runnable() {
+ .setListener(new AnimatorListenerAdapter() {
+ private boolean hasEnded;
+
+ // We use the animation listener instead of withEndAction() to ensure that
+ // onAnimationEnd() is called when the animator is cancelled
@Override
- public void run() {
- // Decrement the post animation trigger
+ public void onAnimationEnd(Animator animation) {
+ if (hasEnded) return;
ctx.postAnimationTrigger.decrement();
+ hasEnded = true;
}
})
+ .setInterpolator(mQuintOutInterpolator)
+ .setDuration(taskViewEnterFromHomeDuration +
+ frontIndex * taskViewEnterFromHomeStaggerDelay)
.start();
ctx.postAnimationTrigger.increment();
}
}
+ public void cancelEnterRecentsAnimation() {
+ animate().cancel();
+ }
+
public void fadeInActionButton(int duration) {
// Hide the action button
mActionButtonView.setAlpha(0f);