- Removed some unused error detection event tracking - Updated incorrect event tracking - Added debug new debug gesture logs Bug: 254250355 Test: attempted several gestures and checked logs Change-Id: Ia1291a08a9a5842addb93c30eb6d21c6c60916c7
458 lines
17 KiB
Java
458 lines
17 KiB
Java
/*
|
|
* Copyright (C) 2019 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 static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
|
|
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
|
|
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
|
|
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
|
|
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET;
|
|
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_HOME;
|
|
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_NEW_TASK;
|
|
|
|
import android.annotation.Nullable;
|
|
import android.annotation.TargetApi;
|
|
import android.content.Intent;
|
|
import android.os.Build;
|
|
import android.view.RemoteAnimationTarget;
|
|
|
|
import com.android.launcher3.statemanager.BaseState;
|
|
import com.android.launcher3.statemanager.StatefulActivity;
|
|
import com.android.launcher3.tracing.GestureStateProto;
|
|
import com.android.launcher3.tracing.SwipeHandlerProto;
|
|
import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
|
|
import com.android.quickstep.util.ActiveGestureErrorDetector;
|
|
import com.android.quickstep.util.ActiveGestureLog;
|
|
import com.android.systemui.shared.recents.model.ThumbnailData;
|
|
|
|
import java.io.PrintWriter;
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
|
|
/**
|
|
* Manages the state for an active system gesture, listens for events from the system and Launcher,
|
|
* and fires events when the states change.
|
|
*/
|
|
@TargetApi(Build.VERSION_CODES.R)
|
|
public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationListener {
|
|
|
|
/**
|
|
* Defines the end targets of a gesture and the associated state.
|
|
*/
|
|
public enum GestureEndTarget {
|
|
HOME(true, LAUNCHER_STATE_HOME, false, GestureStateProto.GestureEndTarget.HOME),
|
|
|
|
RECENTS(true, LAUNCHER_STATE_OVERVIEW, true, GestureStateProto.GestureEndTarget.RECENTS),
|
|
|
|
NEW_TASK(false, LAUNCHER_STATE_BACKGROUND, true,
|
|
GestureStateProto.GestureEndTarget.NEW_TASK),
|
|
|
|
LAST_TASK(false, LAUNCHER_STATE_BACKGROUND, true,
|
|
GestureStateProto.GestureEndTarget.LAST_TASK);
|
|
|
|
GestureEndTarget(boolean isLauncher, int containerType, boolean recentsAttachedToAppWindow,
|
|
GestureStateProto.GestureEndTarget protoEndTarget) {
|
|
this.isLauncher = isLauncher;
|
|
this.containerType = containerType;
|
|
this.recentsAttachedToAppWindow = recentsAttachedToAppWindow;
|
|
this.protoEndTarget = protoEndTarget;
|
|
}
|
|
|
|
/** Whether the target is in the launcher activity. Implicitly, if the end target is going
|
|
to Launcher, then we can not interrupt the animation to start another gesture. */
|
|
public final boolean isLauncher;
|
|
/** Used to log where the user ended up after the gesture ends */
|
|
public final int containerType;
|
|
/** Whether RecentsView should be attached to the window as we animate to this target */
|
|
public final boolean recentsAttachedToAppWindow;
|
|
/** The GestureStateProto enum value, used for winscope tracing. See launcher_trace.proto */
|
|
public final GestureStateProto.GestureEndTarget protoEndTarget;
|
|
}
|
|
|
|
private static final String TAG = "GestureState";
|
|
|
|
private static final List<String> STATE_NAMES = new ArrayList<>();
|
|
public static final GestureState DEFAULT_STATE = new GestureState();
|
|
|
|
private static int FLAG_COUNT = 0;
|
|
private static int getNextStateFlag(String name) {
|
|
if (DEBUG_STATES) {
|
|
STATE_NAMES.add(name);
|
|
}
|
|
int index = 1 << FLAG_COUNT;
|
|
FLAG_COUNT++;
|
|
return index;
|
|
}
|
|
|
|
// Called when the end target as been set
|
|
public static final int STATE_END_TARGET_SET =
|
|
getNextStateFlag("STATE_END_TARGET_SET");
|
|
|
|
// Called when the end target animation has finished
|
|
public static final int STATE_END_TARGET_ANIMATION_FINISHED =
|
|
getNextStateFlag("STATE_END_TARGET_ANIMATION_FINISHED");
|
|
|
|
// Called when the recents animation has been requested to start
|
|
public static final int STATE_RECENTS_ANIMATION_INITIALIZED =
|
|
getNextStateFlag("STATE_RECENTS_ANIMATION_INITIALIZED");
|
|
|
|
// Called when the recents animation is started and the TaskAnimationManager has been updated
|
|
// with the controller and targets
|
|
public static final int STATE_RECENTS_ANIMATION_STARTED =
|
|
getNextStateFlag("STATE_RECENTS_ANIMATION_STARTED");
|
|
|
|
// Called when the recents animation is canceled
|
|
public static final int STATE_RECENTS_ANIMATION_CANCELED =
|
|
getNextStateFlag("STATE_RECENTS_ANIMATION_CANCELED");
|
|
|
|
// Called when the recents animation finishes
|
|
public static final int STATE_RECENTS_ANIMATION_FINISHED =
|
|
getNextStateFlag("STATE_RECENTS_ANIMATION_FINISHED");
|
|
|
|
// Always called when the recents animation ends (regardless of cancel or finish)
|
|
public static final int STATE_RECENTS_ANIMATION_ENDED =
|
|
getNextStateFlag("STATE_RECENTS_ANIMATION_ENDED");
|
|
|
|
// Called when RecentsView stops scrolling and settles on a TaskView.
|
|
public static final int STATE_RECENTS_SCROLLING_FINISHED =
|
|
getNextStateFlag("STATE_RECENTS_SCROLLING_FINISHED");
|
|
|
|
// Needed to interact with the current activity
|
|
private final Intent mHomeIntent;
|
|
private final Intent mOverviewIntent;
|
|
private final BaseActivityInterface mActivityInterface;
|
|
private final MultiStateCallback mStateCallback;
|
|
private final int mGestureId;
|
|
|
|
private CachedTaskInfo mRunningTask;
|
|
private GestureEndTarget mEndTarget;
|
|
private RemoteAnimationTarget mLastAppearedTaskTarget;
|
|
private Set<Integer> mPreviouslyAppearedTaskIds = new HashSet<>();
|
|
private int mLastStartedTaskId = -1;
|
|
private RecentsAnimationController mRecentsAnimationController;
|
|
private HashMap<Integer, ThumbnailData> mRecentsAnimationCanceledSnapshots;
|
|
|
|
/** The time when the swipe up gesture is triggered. */
|
|
private long mSwipeUpStartTimeMs;
|
|
|
|
private boolean mHandlingAtomicEvent;
|
|
|
|
public GestureState(OverviewComponentObserver componentObserver, int gestureId) {
|
|
mHomeIntent = componentObserver.getHomeIntent();
|
|
mOverviewIntent = componentObserver.getOverviewIntent();
|
|
mActivityInterface = componentObserver.getActivityInterface();
|
|
mStateCallback = new MultiStateCallback(
|
|
STATE_NAMES.toArray(new String[0]), GestureState::getTrackedEventForState);
|
|
mGestureId = gestureId;
|
|
}
|
|
|
|
public GestureState(GestureState other) {
|
|
mHomeIntent = other.mHomeIntent;
|
|
mOverviewIntent = other.mOverviewIntent;
|
|
mActivityInterface = other.mActivityInterface;
|
|
mStateCallback = other.mStateCallback;
|
|
mGestureId = other.mGestureId;
|
|
mRunningTask = other.mRunningTask;
|
|
mEndTarget = other.mEndTarget;
|
|
mLastAppearedTaskTarget = other.mLastAppearedTaskTarget;
|
|
mPreviouslyAppearedTaskIds = other.mPreviouslyAppearedTaskIds;
|
|
mLastStartedTaskId = other.mLastStartedTaskId;
|
|
}
|
|
|
|
public GestureState() {
|
|
// Do nothing, only used for initializing the gesture state prior to user unlock
|
|
mHomeIntent = new Intent();
|
|
mOverviewIntent = new Intent();
|
|
mActivityInterface = null;
|
|
mStateCallback = new MultiStateCallback(
|
|
STATE_NAMES.toArray(new String[0]), GestureState::getTrackedEventForState);
|
|
mGestureId = -1;
|
|
}
|
|
|
|
@Nullable
|
|
private static ActiveGestureErrorDetector.GestureEvent getTrackedEventForState(int stateFlag) {
|
|
if (stateFlag == STATE_END_TARGET_ANIMATION_FINISHED) {
|
|
return ActiveGestureErrorDetector.GestureEvent.STATE_END_TARGET_ANIMATION_FINISHED;
|
|
} else if (stateFlag == STATE_RECENTS_SCROLLING_FINISHED) {
|
|
return ActiveGestureErrorDetector.GestureEvent.STATE_RECENTS_SCROLLING_FINISHED;
|
|
} else if (stateFlag == STATE_RECENTS_ANIMATION_CANCELED) {
|
|
return ActiveGestureErrorDetector.GestureEvent.STATE_RECENTS_ANIMATION_CANCELED;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @return whether the gesture state has the provided {@param stateMask} flags set.
|
|
*/
|
|
public boolean hasState(int stateMask) {
|
|
return mStateCallback.hasStates(stateMask);
|
|
}
|
|
|
|
/**
|
|
* Sets the given {@param stateFlag}s.
|
|
*/
|
|
public void setState(int stateFlag) {
|
|
mStateCallback.setState(stateFlag);
|
|
}
|
|
|
|
/**
|
|
* Adds a callback for when the states matching the given {@param stateMask} is set.
|
|
*/
|
|
public void runOnceAtState(int stateMask, Runnable callback) {
|
|
mStateCallback.runOnceAtState(stateMask, callback);
|
|
}
|
|
|
|
/**
|
|
* @return the intent for the Home component.
|
|
*/
|
|
public Intent getHomeIntent() {
|
|
return mHomeIntent;
|
|
}
|
|
|
|
/**
|
|
* @return the intent for the Overview component.
|
|
*/
|
|
public Intent getOverviewIntent() {
|
|
return mOverviewIntent;
|
|
}
|
|
|
|
/**
|
|
* @return the interface to the activity handing the UI updates for this gesture.
|
|
*/
|
|
public <S extends BaseState<S>,
|
|
T extends StatefulActivity<S>> BaseActivityInterface<S, T> getActivityInterface() {
|
|
return mActivityInterface;
|
|
}
|
|
|
|
/**
|
|
* @return the id for this particular gesture.
|
|
*/
|
|
public int getGestureId() {
|
|
return mGestureId;
|
|
}
|
|
|
|
/**
|
|
* @return the running task for this gesture.
|
|
*/
|
|
public CachedTaskInfo getRunningTask() {
|
|
return mRunningTask;
|
|
}
|
|
|
|
/**
|
|
* @return the running task id for this gesture.
|
|
*/
|
|
public int getRunningTaskId() {
|
|
return mRunningTask != null ? mRunningTask.getTaskId() : -1;
|
|
}
|
|
|
|
/**
|
|
* Updates the running task for the gesture to be the given {@param runningTask}.
|
|
*/
|
|
public void updateRunningTask(CachedTaskInfo runningTask) {
|
|
mRunningTask = runningTask;
|
|
}
|
|
|
|
/**
|
|
* Updates the last task that appeared during this gesture.
|
|
*/
|
|
public void updateLastAppearedTaskTarget(RemoteAnimationTarget lastAppearedTaskTarget) {
|
|
mLastAppearedTaskTarget = lastAppearedTaskTarget;
|
|
if (lastAppearedTaskTarget != null) {
|
|
mPreviouslyAppearedTaskIds.add(lastAppearedTaskTarget.taskId);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return The id of the task that appeared during this gesture.
|
|
*/
|
|
public int getLastAppearedTaskId() {
|
|
return mLastAppearedTaskTarget != null ? mLastAppearedTaskTarget.taskId : -1;
|
|
}
|
|
|
|
public void updatePreviouslyAppearedTaskIds(Set<Integer> previouslyAppearedTaskIds) {
|
|
mPreviouslyAppearedTaskIds = previouslyAppearedTaskIds;
|
|
}
|
|
|
|
public Set<Integer> getPreviouslyAppearedTaskIds() {
|
|
return mPreviouslyAppearedTaskIds;
|
|
}
|
|
|
|
/**
|
|
* Updates the last task that we started via startActivityFromRecents() during this gesture.
|
|
*/
|
|
public void updateLastStartedTaskId(int lastStartedTaskId) {
|
|
mLastStartedTaskId = lastStartedTaskId;
|
|
}
|
|
|
|
/**
|
|
* @return The id of the task that was most recently started during this gesture, or -1 if
|
|
* no task has been started yet (i.e. we haven't settled on a new task).
|
|
*/
|
|
public int getLastStartedTaskId() {
|
|
return mLastStartedTaskId;
|
|
}
|
|
|
|
/**
|
|
* @return the end target for this gesture (if known).
|
|
*/
|
|
public GestureEndTarget getEndTarget() {
|
|
return mEndTarget;
|
|
}
|
|
|
|
/**
|
|
* Sets the end target of this gesture and immediately notifies the state changes.
|
|
*/
|
|
public void setEndTarget(GestureEndTarget target) {
|
|
setEndTarget(target, true /* isAtomic */);
|
|
}
|
|
|
|
/**
|
|
* Sets the end target of this gesture, but if {@param isAtomic} is {@code false}, then the
|
|
* caller must explicitly set {@link #STATE_END_TARGET_ANIMATION_FINISHED} themselves.
|
|
*/
|
|
public void setEndTarget(GestureEndTarget target, boolean isAtomic) {
|
|
mEndTarget = target;
|
|
mStateCallback.setState(STATE_END_TARGET_SET);
|
|
ActiveGestureLog.INSTANCE.addLog(
|
|
/* event= */ "setEndTarget " + mEndTarget,
|
|
/* gestureEvent= */ SET_END_TARGET);
|
|
switch (mEndTarget) {
|
|
case HOME:
|
|
ActiveGestureLog.INSTANCE.trackEvent(SET_END_TARGET_HOME);
|
|
break;
|
|
case NEW_TASK:
|
|
ActiveGestureLog.INSTANCE.trackEvent(SET_END_TARGET_NEW_TASK);
|
|
break;
|
|
case LAST_TASK:
|
|
case RECENTS:
|
|
default:
|
|
// No-Op
|
|
}
|
|
if (isAtomic) {
|
|
mStateCallback.setState(STATE_END_TARGET_ANIMATION_FINISHED);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Indicates if the gesture is handling an atomic event like a click and not a
|
|
* user controlled gesture.
|
|
*/
|
|
public void setHandlingAtomicEvent(boolean handlingAtomicEvent) {
|
|
mHandlingAtomicEvent = handlingAtomicEvent;
|
|
}
|
|
|
|
/**
|
|
* Returns true if the gesture is handling an atomic event like a click and not a
|
|
* user controlled gesture.
|
|
*/
|
|
public boolean isHandlingAtomicEvent() {
|
|
return mHandlingAtomicEvent;
|
|
}
|
|
|
|
/**
|
|
* @return whether the current gesture is still running a recents animation to a state in the
|
|
* Launcher or Recents activity.
|
|
*/
|
|
public boolean isRunningAnimationToLauncher() {
|
|
return isRecentsAnimationRunning() && mEndTarget != null && mEndTarget.isLauncher;
|
|
}
|
|
|
|
/**
|
|
* @return whether the recents animation is started but not yet ended
|
|
*/
|
|
public boolean isRecentsAnimationRunning() {
|
|
return mStateCallback.hasStates(STATE_RECENTS_ANIMATION_STARTED)
|
|
&& !mStateCallback.hasStates(STATE_RECENTS_ANIMATION_ENDED);
|
|
}
|
|
|
|
@Override
|
|
public void onRecentsAnimationStart(RecentsAnimationController controller,
|
|
RecentsAnimationTargets targets) {
|
|
mRecentsAnimationController = controller;
|
|
mStateCallback.setState(STATE_RECENTS_ANIMATION_STARTED);
|
|
}
|
|
|
|
@Override
|
|
public void onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
|
|
mRecentsAnimationCanceledSnapshots = thumbnailDatas;
|
|
mStateCallback.setState(STATE_RECENTS_ANIMATION_CANCELED);
|
|
mStateCallback.setState(STATE_RECENTS_ANIMATION_ENDED);
|
|
if (mRecentsAnimationCanceledSnapshots != null) {
|
|
// Clean up the screenshot to finalize the recents animation cancel
|
|
if (mRecentsAnimationController != null) {
|
|
mRecentsAnimationController.cleanupScreenshot();
|
|
}
|
|
mRecentsAnimationCanceledSnapshots = null;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onRecentsAnimationFinished(RecentsAnimationController controller) {
|
|
mStateCallback.setState(STATE_RECENTS_ANIMATION_FINISHED);
|
|
mStateCallback.setState(STATE_RECENTS_ANIMATION_ENDED);
|
|
}
|
|
|
|
/**
|
|
* Returns and clears the canceled animation thumbnail data. This call only returns a value
|
|
* while STATE_RECENTS_ANIMATION_CANCELED state is being set, and the caller is responsible for
|
|
* calling {@link RecentsAnimationController#cleanupScreenshot()}.
|
|
*/
|
|
@Nullable
|
|
HashMap<Integer, ThumbnailData> consumeRecentsAnimationCanceledSnapshot() {
|
|
if (mRecentsAnimationCanceledSnapshots != null) {
|
|
HashMap<Integer, ThumbnailData> data =
|
|
new HashMap<Integer, ThumbnailData>(mRecentsAnimationCanceledSnapshots);
|
|
mRecentsAnimationCanceledSnapshots = null;
|
|
return data;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
void setSwipeUpStartTimeMs(long uptimeMs) {
|
|
mSwipeUpStartTimeMs = uptimeMs;
|
|
}
|
|
|
|
long getSwipeUpStartTimeMs() {
|
|
return mSwipeUpStartTimeMs;
|
|
}
|
|
|
|
public void dump(PrintWriter pw) {
|
|
pw.println("GestureState:");
|
|
pw.println(" gestureID=" + mGestureId);
|
|
pw.println(" runningTask=" + mRunningTask);
|
|
pw.println(" endTarget=" + mEndTarget);
|
|
pw.println(" lastAppearedTaskTargetId=" + getLastAppearedTaskId());
|
|
pw.println(" lastStartedTaskId=" + mLastStartedTaskId);
|
|
pw.println(" isRecentsAnimationRunning=" + isRecentsAnimationRunning());
|
|
}
|
|
|
|
/**
|
|
* Used for winscope tracing, see launcher_trace.proto
|
|
* @see com.android.systemui.shared.tracing.ProtoTraceable#writeToProto
|
|
* @param swipeHandlerProto The parent of this proto message.
|
|
*/
|
|
public void writeToProto(SwipeHandlerProto.Builder swipeHandlerProto) {
|
|
GestureStateProto.Builder gestureStateProto = GestureStateProto.newBuilder();
|
|
gestureStateProto.setEndTarget(mEndTarget == null
|
|
? GestureStateProto.GestureEndTarget.UNSET
|
|
: mEndTarget.protoEndTarget);
|
|
swipeHandlerProto.setGestureState(gestureStateProto);
|
|
}
|
|
}
|