Merge "Ensuring that we finish the last transition before starting a new one." into ub-launcher3-master

This commit is contained in:
Sunny Goyal 2018-03-12 23:22:00 +00:00 committed by Android (Google) Code Review
commit 7eee62b313
10 changed files with 192 additions and 31 deletions

View File

@ -77,6 +77,12 @@ public class AnimatedFloat {
}
}
public void finishAnimation() {
if (mValueAnimator != null && mValueAnimator.isRunning()) {
mValueAnimator.end();
}
}
public ObjectAnimator getCurrentAnimation() {
return mValueAnimator;
}

View File

@ -23,6 +23,7 @@ import com.android.quickstep.TouchConsumer.InteractionType;
public abstract class BaseSwipeInteractionHandler extends InternalStateHandler {
protected Runnable mGestureEndCallback;
protected boolean mIsGoingToHome;
public void setGestureEndCallback(Runnable gestureEndCallback) {
mGestureEndCallback = gestureEndCallback;

View File

@ -0,0 +1,99 @@
/*
* Copyright (C) 2018 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.
*/
package com.android.quickstep;
import android.annotation.TargetApi;
import android.os.Build;
import android.view.Choreographer;
import android.view.MotionEvent;
import android.view.VelocityTracker;
/**
* A TouchConsumer which defers all events on the UIThread until the consumer is created.
*/
@TargetApi(Build.VERSION_CODES.P)
public class DeferredTouchConsumer implements TouchConsumer {
private final VelocityTracker mVelocityTracker;
private final DeferredTouchProvider mTouchProvider;
private MotionEventQueue mMyQueue;
private TouchConsumer mTarget;
public DeferredTouchConsumer(DeferredTouchProvider touchProvider) {
mVelocityTracker = VelocityTracker.obtain();
mTouchProvider = touchProvider;
}
@Override
public void accept(MotionEvent event) {
mTarget.accept(event);
}
@Override
public void reset() {
mTarget.reset();
}
@Override
public void updateTouchTracking(int interactionType) {
mTarget.updateTouchTracking(interactionType);
}
@Override
public void onQuickScrubEnd() {
mTarget.onQuickScrubEnd();
}
@Override
public void onQuickScrubProgress(float progress) {
mTarget.onQuickScrubProgress(progress);
}
@Override
public void preProcessMotionEvent(MotionEvent ev) {
mVelocityTracker.addMovement(ev);
}
@Override
public Choreographer getIntrimChoreographer(MotionEventQueue queue) {
mMyQueue = queue;
return null;
}
@Override
public void deferInit() {
mTarget = mTouchProvider.createTouchConsumer(mVelocityTracker);
mTarget.getIntrimChoreographer(mMyQueue);
}
@Override
public boolean forceToLauncherConsumer() {
return mTarget.forceToLauncherConsumer();
}
@Override
public boolean deferNextEventToMainThread() {
// If our target is still null, defer the next target as well
TouchConsumer target = mTarget;
return target == null ? true : target.deferNextEventToMainThread();
}
public interface DeferredTouchProvider {
TouchConsumer createTouchConsumer(VelocityTracker tracker);
}
}

View File

@ -53,6 +53,8 @@ public class MotionEventQueue {
ACTION_VIRTUAL | (4 << ACTION_POINTER_INDEX_SHIFT);
private static final int ACTION_RESET =
ACTION_VIRTUAL | (5 << ACTION_POINTER_INDEX_SHIFT);
private static final int ACTION_DEFER_INIT =
ACTION_VIRTUAL | (6 << ACTION_POINTER_INDEX_SHIFT);
private final EventArray mEmptyArray = new EventArray();
private final Object mExecutionLock = new Object();
@ -76,10 +78,10 @@ public class MotionEventQueue {
public MotionEventQueue(Choreographer choreographer, TouchConsumer consumer) {
mMainChoreographer = choreographer;
mConsumer = consumer;
mCurrentChoreographer = mMainChoreographer;
mCurrentRunnable = mMainFrameCallback;
setInterimChoreographerLocked(consumer.getIntrimChoreographer(this));
setInterimChoreographer(consumer.getIntrimChoreographer(this));
}
public void setInterimChoreographer(Choreographer choreographer) {
@ -156,6 +158,9 @@ public class MotionEventQueue {
case ACTION_RESET:
mConsumer.reset();
break;
case ACTION_DEFER_INIT:
mConsumer.deferInit();
break;
default:
Log.e(TAG, "Invalid virtual event: " + event.getAction());
}
@ -204,6 +209,14 @@ public class MotionEventQueue {
queueVirtualAction(ACTION_RESET, 0);
}
public void deferInit() {
queueVirtualAction(ACTION_DEFER_INIT, 0);
}
public TouchConsumer getConsumer() {
return mConsumer;
}
private static class EventArray extends ArrayList<MotionEvent> {
public int lastEventAction = ACTION_CANCEL;

View File

@ -319,13 +319,14 @@ public class NavBarSwipeInteractionHandler extends BaseSwipeInteractionHandler i
/** Animates to the given progress, where 0 is the current app and 1 is overview. */
private void animateToProgress(float progress, long duration) {
mIsGoingToHome = Float.compare(progress, 1) == 0;
ObjectAnimator anim = mCurrentShift.animateToValue(progress).setDuration(duration);
anim.setInterpolator(Interpolators.SCROLL);
anim.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
mStateCallback.setState((Float.compare(mCurrentShift.value, 0) == 0)
? STATE_SCALED_SNAPSHOT_APP : STATE_SCALED_SNAPSHOT_RECENTS);
mStateCallback.setState(mIsGoingToHome
? STATE_SCALED_SNAPSHOT_RECENTS : STATE_SCALED_SNAPSHOT_APP);
}
});
anim.start();

View File

@ -25,9 +25,9 @@ import static android.view.MotionEvent.INVALID_POINTER_ID;
import static com.android.quickstep.RemoteRunnable.executeSafely;
import static com.android.quickstep.TouchInteractionService.DEBUG_SHOW_OVERVIEW_BUTTON;
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_BACK;
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_OVERVIEW;
import android.annotation.TargetApi;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityOptions;
import android.content.Context;
@ -39,6 +39,7 @@ import android.graphics.Color;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
import android.os.Looper;
import android.util.Log;
@ -70,6 +71,7 @@ import java.util.concurrent.TimeUnit;
/**
* Touch consumer for handling events originating from an activity other than Launcher
*/
@TargetApi(Build.VERSION_CODES.P)
public class OtherActivityTouchConsumer extends ContextWrapper implements TouchConsumer {
private static final String TAG = "ActivityTouchConsumer";
@ -97,16 +99,17 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
private VelocityTracker mVelocityTracker;
private MotionEventQueue mEventQueue;
private boolean mIsGoingToHome;
public OtherActivityTouchConsumer(Context base, RunningTaskInfo runningTaskInfo,
RecentsModel recentsModel, Intent homeIntent, ISystemUiProxy systemUiProxy,
MainThreadExecutor mainThreadExecutor, Choreographer backgroundThreadChoreographer,
@HitTarget int downHitTarget) {
@HitTarget int downHitTarget, VelocityTracker velocityTracker) {
super(base);
mRunningTask = runningTaskInfo;
mRecentsModel = recentsModel;
mHomeIntent = homeIntent;
mVelocityTracker = VelocityTracker.obtain();
mVelocityTracker = velocityTracker;
mISystemUiProxy = systemUiProxy;
mMainThreadExecutor = mainThreadExecutor;
mBackgroundThreadChoreographer = backgroundThreadChoreographer;
@ -374,6 +377,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
if (mInteractionHandler != null) {
final BaseSwipeInteractionHandler handler = mInteractionHandler;
mInteractionHandler = null;
mIsGoingToHome = handler.mIsGoingToHome;
mMainThreadExecutor.execute(handler::reset);
}
}
@ -428,4 +432,15 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
}
}
}
@Override
public boolean forceToLauncherConsumer() {
return mIsGoingToHome;
}
@Override
public boolean deferNextEventToMainThread() {
// TODO: Consider also check if the eventQueue is using mainThread of not.
return mInteractionHandler != null;
}
}

View File

@ -21,8 +21,6 @@ import android.support.annotation.IntDef;
import android.view.Choreographer;
import android.view.MotionEvent;
import com.android.systemui.shared.system.NavigationBarCompat.HitTarget;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.function.Consumer;
@ -64,4 +62,14 @@ public interface TouchConsumer extends Consumer<MotionEvent> {
default Choreographer getIntrimChoreographer(MotionEventQueue queue) {
return null;
}
default void deferInit() { }
default boolean deferNextEventToMainThread() {
return false;
}
default boolean forceToLauncherConsumer() {
return false;
}
}

View File

@ -39,6 +39,7 @@ import android.util.Log;
import android.util.SparseArray;
import android.view.Choreographer;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
@ -161,7 +162,6 @@ public class TouchInteractionService extends Service {
private Choreographer mMainThreadChoreographer;
private Choreographer mBackgroundThreadChoreographer;
private MotionEventQueue mNoOpEventQueue;
@Override
public void onCreate() {
@ -171,8 +171,7 @@ public class TouchInteractionService extends Service {
mMainThreadExecutor = new MainThreadExecutor();
mOverviewCommandHelper = new OverviewCommandHelper(this);
mMainThreadChoreographer = Choreographer.getInstance();
mNoOpEventQueue = new MotionEventQueue(mMainThreadChoreographer, mNoOpTouchConsumer);
mEventQueue = mNoOpEventQueue;
mEventQueue = new MotionEventQueue(mMainThreadChoreographer, mNoOpTouchConsumer);
sConnected = true;
@ -194,31 +193,45 @@ public class TouchInteractionService extends Service {
}
private void onBinderPreMotionEvent(@HitTarget int downHitTarget) {
mEventQueue.reset();
TouchConsumer oldConsumer = mEventQueue.getConsumer();
if (oldConsumer.deferNextEventToMainThread()) {
mEventQueue = new MotionEventQueue(mMainThreadChoreographer,
new DeferredTouchConsumer((v) -> getCurrentTouchConsumer(downHitTarget,
oldConsumer.forceToLauncherConsumer(), v)));
mEventQueue.deferInit();
} else {
mEventQueue = new MotionEventQueue(
mMainThreadChoreographer, getCurrentTouchConsumer(downHitTarget, false, null));
}
}
private TouchConsumer getCurrentTouchConsumer(
@HitTarget int downHitTarget, boolean forceToLauncher, VelocityTracker tracker) {
RunningTaskInfo runningTaskInfo = mAM.getRunningTask();
mEventQueue.reset();
if (runningTaskInfo == null) {
mEventQueue = mNoOpEventQueue;
} else if (runningTaskInfo.topActivity.equals(mOverviewCommandHelper.launcher)) {
mEventQueue = getLauncherEventQueue();
if (runningTaskInfo == null && !forceToLauncher) {
return mNoOpTouchConsumer;
} else if (forceToLauncher ||
runningTaskInfo.topActivity.equals(mOverviewCommandHelper.launcher)) {
return getLauncherConsumer();
} else {
mEventQueue = new MotionEventQueue(mMainThreadChoreographer,
new OtherActivityTouchConsumer(this, runningTaskInfo, mRecentsModel,
if (tracker == null) {
tracker = VelocityTracker.obtain();
}
return new OtherActivityTouchConsumer(this, runningTaskInfo, mRecentsModel,
mOverviewCommandHelper.homeIntent, mISystemUiProxy, mMainThreadExecutor,
mBackgroundThreadChoreographer, downHitTarget));
mBackgroundThreadChoreographer, downHitTarget, tracker);
}
}
private MotionEventQueue getLauncherEventQueue() {
private TouchConsumer getLauncherConsumer() {
Launcher launcher = (Launcher) LauncherAppState.getInstance(this).getModel().getCallback();
if (launcher == null) {
return mNoOpEventQueue;
return mNoOpTouchConsumer;
}
View target = launcher.getDragLayer();
return new MotionEventQueue(mMainThreadChoreographer,
new LauncherTouchConsumer(launcher, target));
return new LauncherTouchConsumer(launcher, target);
}
private static class LauncherTouchConsumer implements TouchConsumer {

View File

@ -725,13 +725,14 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler {
/** Animates to the given progress, where 0 is the current app and 1 is overview. */
private void animateToProgress(float progress, long duration) {
mIsGoingToHome = Float.compare(progress, 1) == 0;
ObjectAnimator anim = mCurrentShift.animateToValue(progress).setDuration(duration);
anim.setInterpolator(Interpolators.SCROLL);
anim.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
setStateOnUiThread((Float.compare(mCurrentShift.value, 0) == 0)
? STATE_SCALED_CONTROLLER_APP : STATE_SCALED_CONTROLLER_RECENTS);
setStateOnUiThread(mIsGoingToHome ?
STATE_SCALED_CONTROLLER_RECENTS : STATE_SCALED_CONTROLLER_APP);
}
});
anim.start();
@ -752,7 +753,7 @@ public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler {
}
private void invalidateHandler() {
mCurrentShift.cancelAnimation();
mCurrentShift.finishAnimation();
if (mGestureEndCallback != null) {
mGestureEndCallback.run();

View File

@ -333,6 +333,10 @@ public class LauncherStateManager {
}
public void moveToRestState() {
if (mConfig.mCurrentAnimation != null && mConfig.userControlled) {
// The user is doing something. Lets not mess it up
return;
}
if (mState.disableRestore) {
goToState(getRestState());
// Reset history