script-astra/Android/Sdk/sources/android-35/android/window/BackTouchTracker.java

276 lines
9.6 KiB
Java
Raw Normal View History

2025-01-20 15:15:20 +00:00
/*
* Copyright (C) 2022 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 android.window;
import android.annotation.FloatRange;
import android.os.SystemProperties;
import android.util.MathUtils;
import android.view.MotionEvent;
import android.view.RemoteAnimationTarget;
import java.io.PrintWriter;
/**
* Helper class to record the touch location for gesture and generate back events.
* @hide
*/
public class BackTouchTracker {
private static final String PREDICTIVE_BACK_LINEAR_DISTANCE_PROP =
"persist.wm.debug.predictive_back_linear_distance";
private static final int LINEAR_DISTANCE = SystemProperties
.getInt(PREDICTIVE_BACK_LINEAR_DISTANCE_PROP, -1);
private float mLinearDistance = LINEAR_DISTANCE;
private float mMaxDistance;
private float mNonLinearFactor;
/**
* Location of the latest touch event
*/
private float mLatestTouchX;
private float mLatestTouchY;
private boolean mTriggerBack;
/**
* Location of the initial touch event of the back gesture.
*/
private float mInitTouchX;
private float mInitTouchY;
private float mLatestVelocityX;
private float mLatestVelocityY;
private float mStartThresholdX;
private int mSwipeEdge;
private boolean mShouldUpdateStartLocation = false;
private TouchTrackerState mState = TouchTrackerState.INITIAL;
/**
* Updates the tracker with a new motion event.
*/
public void update(float touchX, float touchY, float velocityX, float velocityY) {
/**
* If back was previously cancelled but the user has started swiping in the forward
* direction again, restart back.
*/
if ((touchX < mStartThresholdX && mSwipeEdge == BackEvent.EDGE_LEFT)
|| (touchX > mStartThresholdX && mSwipeEdge == BackEvent.EDGE_RIGHT)) {
mStartThresholdX = touchX;
if ((mSwipeEdge == BackEvent.EDGE_LEFT && mStartThresholdX < mInitTouchX)
|| (mSwipeEdge == BackEvent.EDGE_RIGHT && mStartThresholdX > mInitTouchX)) {
mInitTouchX = mStartThresholdX;
}
}
mLatestTouchX = touchX;
mLatestTouchY = touchY;
mLatestVelocityX = velocityX;
mLatestVelocityY = velocityY;
}
/** Sets whether the back gesture is past the trigger threshold. */
public void setTriggerBack(boolean triggerBack) {
if (mTriggerBack != triggerBack && !triggerBack) {
mStartThresholdX = mLatestTouchX;
}
mTriggerBack = triggerBack;
}
/** Gets whether the back gesture is past the trigger threshold. */
public boolean getTriggerBack() {
return mTriggerBack;
}
/** Returns if the start location should be updated. */
public boolean shouldUpdateStartLocation() {
return mShouldUpdateStartLocation;
}
/** Sets if the start location should be updated. */
public void setShouldUpdateStartLocation(boolean shouldUpdate) {
mShouldUpdateStartLocation = shouldUpdate;
}
/** Sets the state of the touch tracker. */
public void setState(TouchTrackerState state) {
mState = state;
}
/** Returns if the tracker is in initial state. */
public boolean isInInitialState() {
return mState == TouchTrackerState.INITIAL;
}
/** Returns if a back gesture is active. */
public boolean isActive() {
return mState == TouchTrackerState.ACTIVE;
}
/** Returns if a back gesture has been finished. */
public boolean isFinished() {
return mState == TouchTrackerState.FINISHED;
}
/** Sets the start location of the back gesture. */
public void setGestureStartLocation(float touchX, float touchY, int swipeEdge) {
mInitTouchX = touchX;
mInitTouchY = touchY;
mLatestTouchX = touchX;
mLatestTouchY = touchY;
mSwipeEdge = swipeEdge;
mStartThresholdX = mInitTouchX;
}
/** Update the start location used to compute the progress to the latest touch location. */
public void updateStartLocation() {
mInitTouchX = mLatestTouchX;
mInitTouchY = mLatestTouchY;
mStartThresholdX = mInitTouchX;
mShouldUpdateStartLocation = false;
}
/** Resets the tracker. */
public void reset() {
mInitTouchX = 0;
mInitTouchY = 0;
mStartThresholdX = 0;
mTriggerBack = false;
mState = TouchTrackerState.INITIAL;
mSwipeEdge = BackEvent.EDGE_LEFT;
mShouldUpdateStartLocation = false;
}
/** Creates a start {@link BackMotionEvent}. */
public BackMotionEvent createStartEvent(RemoteAnimationTarget target) {
return new BackMotionEvent(
/* touchX = */ mInitTouchX,
/* touchY = */ mInitTouchY,
/* progress = */ 0,
/* velocityX = */ 0,
/* velocityY = */ 0,
/* triggerBack = */ mTriggerBack,
/* swipeEdge = */ mSwipeEdge,
/* departingAnimationTarget = */ target);
}
/** Creates a progress {@link BackMotionEvent}. */
public BackMotionEvent createProgressEvent() {
float progress = getProgress(mLatestTouchX);
return createProgressEvent(progress);
}
/**
* Progress value computed from the touch position.
*
* @param touchX the X touch position of the {@link MotionEvent}.
* @return progress value
*/
@FloatRange(from = 0.0, to = 1.0)
public float getProgress(float touchX) {
// If back is committed, progress is the distance between the last and first touch
// point, divided by the max drag distance. Otherwise, it's the distance between
// the last touch point and the starting threshold, divided by max drag distance.
// The starting threshold is initially the first touch location, and updated to
// the location everytime back is restarted after being cancelled.
float startX = mTriggerBack ? mInitTouchX : mStartThresholdX;
float distance;
if (mSwipeEdge == BackEvent.EDGE_LEFT) {
distance = touchX - startX;
} else {
distance = startX - touchX;
}
float deltaX = Math.max(0f, distance);
float linearDistance = mLinearDistance;
float maxDistance = getMaxDistance();
maxDistance = maxDistance == 0 ? 1 : maxDistance;
float progress;
if (linearDistance < maxDistance) {
// Up to linearDistance it behaves linearly, then slowly reaches 1f.
// maxDistance is composed of linearDistance + nonLinearDistance
float nonLinearDistance = maxDistance - linearDistance;
float initialTarget = linearDistance + nonLinearDistance * mNonLinearFactor;
boolean isLinear = deltaX <= linearDistance;
if (isLinear) {
progress = deltaX / initialTarget;
} else {
float nonLinearDeltaX = deltaX - linearDistance;
float nonLinearProgress = nonLinearDeltaX / nonLinearDistance;
float currentTarget = MathUtils.lerp(
/* start = */ initialTarget,
/* stop = */ maxDistance,
/* amount = */ nonLinearProgress);
progress = deltaX / currentTarget;
}
} else {
// Always linear behavior.
progress = deltaX / maxDistance;
}
return MathUtils.constrain(progress, 0, 1);
}
/**
* Maximum distance in pixels.
* Progress is considered to be completed (1f) when this limit is exceeded.
*/
public float getMaxDistance() {
return mMaxDistance;
}
public float getLinearDistance() {
return mLinearDistance;
}
public float getNonLinearFactor() {
return mNonLinearFactor;
}
/** Creates a progress {@link BackMotionEvent} for the given progress. */
public BackMotionEvent createProgressEvent(float progress) {
return new BackMotionEvent(
/* touchX = */ mLatestTouchX,
/* touchY = */ mLatestTouchY,
/* progress = */ progress,
/* velocityX = */ mLatestVelocityX,
/* velocityY = */ mLatestVelocityY,
/* triggerBack = */ mTriggerBack,
/* swipeEdge = */ mSwipeEdge,
/* departingAnimationTarget = */ null);
}
/** Sets the thresholds for computing progress. */
public void setProgressThresholds(float linearDistance, float maxDistance,
float nonLinearFactor) {
if (LINEAR_DISTANCE >= 0) {
mLinearDistance = LINEAR_DISTANCE;
} else {
mLinearDistance = linearDistance;
}
mMaxDistance = maxDistance;
mNonLinearFactor = nonLinearFactor;
}
/** Dumps debugging info. */
public void dump(PrintWriter pw, String prefix) {
pw.println(prefix + "BackTouchTracker state:");
pw.println(prefix + " mState=" + mState);
pw.println(prefix + " mTriggerBack=" + mTriggerBack);
}
public enum TouchTrackerState {
INITIAL, ACTIVE, FINISHED
}
}