Merge "Base: Predictive Back, add support for large screens when swiping" into udc-dev am: b067a895d1
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/22738467 Change-Id: I0955c3cc702b00af956d29b1a0516730769ff50a Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -54,10 +54,42 @@ public interface BackAnimation {
|
|||||||
void setTriggerBack(boolean triggerBack);
|
void setTriggerBack(boolean triggerBack);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the threshold values that defining edge swipe behavior.
|
* Sets the threshold values that define edge swipe behavior.<br>
|
||||||
* @param progressThreshold the max threshold to keep linear progressing back animation.
|
* <br>
|
||||||
|
* <h1>How does {@code nonLinearFactor} work?</h1>
|
||||||
|
* <pre>
|
||||||
|
* screen screen screen
|
||||||
|
* width width width
|
||||||
|
* |——————| |————————————| |————————————————————|
|
||||||
|
* A B A B C A
|
||||||
|
* 1 +——————+—————+ 1 +————————————+ 1 +————————————+———————+
|
||||||
|
* | / | | —/| | | —————/|
|
||||||
|
* | / | | —/ | | ——/ |
|
||||||
|
* | / | | —/ | | ——/ | |
|
||||||
|
* | / | | —/ | | ——/ | |
|
||||||
|
* | / | | —/ | | ——/ | |
|
||||||
|
* |/ | |—/ | |—/ | |
|
||||||
|
* 0 +————————————+ 0 +————————————+ 0 +————————————+———————+
|
||||||
|
* B B B
|
||||||
|
* </pre>
|
||||||
|
* Three devices with different widths (smaller, equal, and wider) relative to the progress
|
||||||
|
* threshold are shown in the graphs.<br>
|
||||||
|
* - A is the width of the screen<br>
|
||||||
|
* - B is the progress threshold (horizontal swipe distance where progress is linear)<br>
|
||||||
|
* - C equals B + (A - B) * nonLinearFactor<br>
|
||||||
|
* <br>
|
||||||
|
* If A is less than or equal to B, {@code progress} for the swipe distance between:<br>
|
||||||
|
* - [0, A] will scale linearly between [0, 1].<br>
|
||||||
|
* If A is greater than B, {@code progress} for swipe distance between:<br>
|
||||||
|
* - [0, B] will scale linearly between [0, B / C]<br>
|
||||||
|
* - (B, A] will scale non-linearly and reach 1.
|
||||||
|
*
|
||||||
|
* @param linearDistance up to this distance progress continues linearly. B in the graph above.
|
||||||
|
* @param maxDistance distance at which the progress will be 1f. A in the graph above.
|
||||||
|
* @param nonLinearFactor This value is used to calculate the target if the screen is wider
|
||||||
|
* than the progress threshold.
|
||||||
*/
|
*/
|
||||||
void setSwipeThresholds(float progressThreshold);
|
void setSwipeThresholds(float linearDistance, float maxDistance, float nonLinearFactor);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the system bar listener to control the system bar color.
|
* Sets the system bar listener to control the system bar color.
|
||||||
|
|||||||
@@ -301,9 +301,12 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setSwipeThresholds(float progressThreshold) {
|
public void setSwipeThresholds(
|
||||||
|
float linearDistance,
|
||||||
|
float maxDistance,
|
||||||
|
float nonLinearFactor) {
|
||||||
mShellExecutor.execute(() -> BackAnimationController.this.setSwipeThresholds(
|
mShellExecutor.execute(() -> BackAnimationController.this.setSwipeThresholds(
|
||||||
progressThreshold));
|
linearDistance, maxDistance, nonLinearFactor));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -509,7 +512,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
|||||||
// Constraints - absolute values
|
// Constraints - absolute values
|
||||||
float minVelocity = mFlingAnimationUtils.getMinVelocityPxPerSecond();
|
float minVelocity = mFlingAnimationUtils.getMinVelocityPxPerSecond();
|
||||||
float maxVelocity = mFlingAnimationUtils.getHighVelocityPxPerSecond();
|
float maxVelocity = mFlingAnimationUtils.getHighVelocityPxPerSecond();
|
||||||
float maxX = mTouchTracker.getMaxX(); // px
|
float maxX = mTouchTracker.getMaxDistance(); // px
|
||||||
float maxFlingDistance = maxX * MAX_FLING_PROGRESS; // px
|
float maxFlingDistance = maxX * MAX_FLING_PROGRESS; // px
|
||||||
|
|
||||||
// Current state
|
// Current state
|
||||||
@@ -605,8 +608,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
|||||||
mTouchTracker.setTriggerBack(triggerBack);
|
mTouchTracker.setTriggerBack(triggerBack);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setSwipeThresholds(float progressThreshold) {
|
private void setSwipeThresholds(
|
||||||
mTouchTracker.setProgressThreshold(progressThreshold);
|
float linearDistance,
|
||||||
|
float maxDistance,
|
||||||
|
float nonLinearFactor) {
|
||||||
|
mTouchTracker.setProgressThresholds(linearDistance, maxDistance, nonLinearFactor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void invokeOrCancelBack() {
|
private void invokeOrCancelBack() {
|
||||||
|
|||||||
@@ -28,11 +28,13 @@ import android.window.BackMotionEvent;
|
|||||||
* Helper class to record the touch location for gesture and generate back events.
|
* Helper class to record the touch location for gesture and generate back events.
|
||||||
*/
|
*/
|
||||||
class TouchTracker {
|
class TouchTracker {
|
||||||
private static final String PREDICTIVE_BACK_PROGRESS_THRESHOLD_PROP =
|
private static final String PREDICTIVE_BACK_LINEAR_DISTANCE_PROP =
|
||||||
"persist.wm.debug.predictive_back_progress_threshold";
|
"persist.wm.debug.predictive_back_linear_distance";
|
||||||
private static final int PROGRESS_THRESHOLD = SystemProperties
|
private static final int LINEAR_DISTANCE = SystemProperties
|
||||||
.getInt(PREDICTIVE_BACK_PROGRESS_THRESHOLD_PROP, -1);
|
.getInt(PREDICTIVE_BACK_LINEAR_DISTANCE_PROP, -1);
|
||||||
private float mProgressThreshold;
|
private float mLinearDistance = LINEAR_DISTANCE;
|
||||||
|
private float mMaxDistance;
|
||||||
|
private float mNonLinearFactor;
|
||||||
/**
|
/**
|
||||||
* Location of the latest touch event
|
* Location of the latest touch event
|
||||||
*/
|
*/
|
||||||
@@ -125,17 +127,42 @@ class TouchTracker {
|
|||||||
// the location everytime back is restarted after being cancelled.
|
// the location everytime back is restarted after being cancelled.
|
||||||
float startX = mTriggerBack ? mInitTouchX : mStartThresholdX;
|
float startX = mTriggerBack ? mInitTouchX : mStartThresholdX;
|
||||||
float deltaX = Math.abs(startX - touchX);
|
float deltaX = Math.abs(startX - touchX);
|
||||||
float maxX = getMaxX();
|
float linearDistance = mLinearDistance;
|
||||||
maxX = maxX == 0 ? 1 : maxX;
|
float maxDistance = getMaxDistance();
|
||||||
return MathUtils.constrain(deltaX / maxX, 0, 1);
|
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 X value (in pixels).
|
* Maximum distance in pixels.
|
||||||
* Progress is considered to be completed (1f) when this limit is exceeded.
|
* Progress is considered to be completed (1f) when this limit is exceeded.
|
||||||
*/
|
*/
|
||||||
float getMaxX() {
|
float getMaxDistance() {
|
||||||
return PROGRESS_THRESHOLD >= 0 ? PROGRESS_THRESHOLD : mProgressThreshold;
|
return mMaxDistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
BackMotionEvent createProgressEvent(float progress) {
|
BackMotionEvent createProgressEvent(float progress) {
|
||||||
@@ -149,7 +176,14 @@ class TouchTracker {
|
|||||||
/* departingAnimationTarget = */ null);
|
/* departingAnimationTarget = */ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setProgressThreshold(float progressThreshold) {
|
public void setProgressThresholds(float linearDistance, float maxDistance,
|
||||||
mProgressThreshold = progressThreshold;
|
float nonLinearFactor) {
|
||||||
|
if (LINEAR_DISTANCE >= 0) {
|
||||||
|
mLinearDistance = LINEAR_DISTANCE;
|
||||||
|
} else {
|
||||||
|
mLinearDistance = linearDistance;
|
||||||
|
}
|
||||||
|
mMaxDistance = maxDistance;
|
||||||
|
mNonLinearFactor = nonLinearFactor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,141 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 com.android.wm.shell.back;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
|
|
||||||
import android.window.BackEvent;
|
|
||||||
import android.window.BackMotionEvent;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
public class TouchTrackerTest {
|
|
||||||
private static final float FAKE_THRESHOLD = 400;
|
|
||||||
private static final float INITIAL_X_LEFT_EDGE = 5;
|
|
||||||
private static final float INITIAL_X_RIGHT_EDGE = FAKE_THRESHOLD - INITIAL_X_LEFT_EDGE;
|
|
||||||
private TouchTracker mTouchTracker;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() throws Exception {
|
|
||||||
mTouchTracker = new TouchTracker();
|
|
||||||
mTouchTracker.setProgressThreshold(FAKE_THRESHOLD);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void generatesProgress_onStart() {
|
|
||||||
mTouchTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0, BackEvent.EDGE_LEFT);
|
|
||||||
BackMotionEvent event = mTouchTracker.createStartEvent(null);
|
|
||||||
assertEquals(event.getProgress(), 0f, 0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void generatesProgress_leftEdge() {
|
|
||||||
mTouchTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0, BackEvent.EDGE_LEFT);
|
|
||||||
float touchX = 10;
|
|
||||||
float velocityX = 0;
|
|
||||||
float velocityY = 0;
|
|
||||||
|
|
||||||
// Pre-commit
|
|
||||||
mTouchTracker.update(touchX, 0, velocityX, velocityY);
|
|
||||||
assertEquals(getProgress(), (touchX - INITIAL_X_LEFT_EDGE) / FAKE_THRESHOLD, 0f);
|
|
||||||
|
|
||||||
// Post-commit
|
|
||||||
touchX += 100;
|
|
||||||
mTouchTracker.setTriggerBack(true);
|
|
||||||
mTouchTracker.update(touchX, 0, velocityX, velocityY);
|
|
||||||
assertEquals(getProgress(), (touchX - INITIAL_X_LEFT_EDGE) / FAKE_THRESHOLD, 0f);
|
|
||||||
|
|
||||||
// Cancel
|
|
||||||
touchX -= 10;
|
|
||||||
mTouchTracker.setTriggerBack(false);
|
|
||||||
mTouchTracker.update(touchX, 0, velocityX, velocityY);
|
|
||||||
assertEquals(getProgress(), 0, 0f);
|
|
||||||
|
|
||||||
// Cancel more
|
|
||||||
touchX -= 10;
|
|
||||||
mTouchTracker.update(touchX, 0, velocityX, velocityY);
|
|
||||||
assertEquals(getProgress(), 0, 0f);
|
|
||||||
|
|
||||||
// Restart
|
|
||||||
touchX += 10;
|
|
||||||
mTouchTracker.update(touchX, 0, velocityX, velocityY);
|
|
||||||
assertEquals(getProgress(), 0, 0f);
|
|
||||||
|
|
||||||
// Restarted, but pre-commit
|
|
||||||
float restartX = touchX;
|
|
||||||
touchX += 10;
|
|
||||||
mTouchTracker.update(touchX, 0, velocityX, velocityY);
|
|
||||||
assertEquals(getProgress(), (touchX - restartX) / FAKE_THRESHOLD, 0f);
|
|
||||||
|
|
||||||
// Restarted, post-commit
|
|
||||||
touchX += 10;
|
|
||||||
mTouchTracker.setTriggerBack(true);
|
|
||||||
mTouchTracker.update(touchX, 0, velocityX, velocityY);
|
|
||||||
assertEquals(getProgress(), (touchX - INITIAL_X_LEFT_EDGE) / FAKE_THRESHOLD, 0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void generatesProgress_rightEdge() {
|
|
||||||
mTouchTracker.setGestureStartLocation(INITIAL_X_RIGHT_EDGE, 0, BackEvent.EDGE_RIGHT);
|
|
||||||
float touchX = INITIAL_X_RIGHT_EDGE - 10; // Fake right edge
|
|
||||||
float velocityX = 0f;
|
|
||||||
float velocityY = 0f;
|
|
||||||
|
|
||||||
// Pre-commit
|
|
||||||
mTouchTracker.update(touchX, 0, velocityX, velocityY);
|
|
||||||
assertEquals(getProgress(), (INITIAL_X_RIGHT_EDGE - touchX) / FAKE_THRESHOLD, 0f);
|
|
||||||
|
|
||||||
// Post-commit
|
|
||||||
touchX -= 100;
|
|
||||||
mTouchTracker.setTriggerBack(true);
|
|
||||||
mTouchTracker.update(touchX, 0, velocityX, velocityY);
|
|
||||||
assertEquals(getProgress(), (INITIAL_X_RIGHT_EDGE - touchX) / FAKE_THRESHOLD, 0f);
|
|
||||||
|
|
||||||
// Cancel
|
|
||||||
touchX += 10;
|
|
||||||
mTouchTracker.setTriggerBack(false);
|
|
||||||
mTouchTracker.update(touchX, 0, velocityX, velocityY);
|
|
||||||
assertEquals(getProgress(), 0, 0f);
|
|
||||||
|
|
||||||
// Cancel more
|
|
||||||
touchX += 10;
|
|
||||||
mTouchTracker.update(touchX, 0, velocityX, velocityY);
|
|
||||||
assertEquals(getProgress(), 0, 0f);
|
|
||||||
|
|
||||||
// Restart
|
|
||||||
touchX -= 10;
|
|
||||||
mTouchTracker.update(touchX, 0, velocityX, velocityY);
|
|
||||||
assertEquals(getProgress(), 0, 0f);
|
|
||||||
|
|
||||||
// Restarted, but pre-commit
|
|
||||||
float restartX = touchX;
|
|
||||||
touchX -= 10;
|
|
||||||
mTouchTracker.update(touchX, 0, velocityX, velocityY);
|
|
||||||
assertEquals(getProgress(), (restartX - touchX) / FAKE_THRESHOLD, 0f);
|
|
||||||
|
|
||||||
// Restarted, post-commit
|
|
||||||
touchX -= 10;
|
|
||||||
mTouchTracker.setTriggerBack(true);
|
|
||||||
mTouchTracker.update(touchX, 0, velocityX, velocityY);
|
|
||||||
assertEquals(getProgress(), (INITIAL_X_RIGHT_EDGE - touchX) / FAKE_THRESHOLD, 0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
private float getProgress() {
|
|
||||||
return mTouchTracker.createProgressEvent().getProgress();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
/*
|
||||||
|
* 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 com.android.wm.shell.back
|
||||||
|
|
||||||
|
import android.util.MathUtils
|
||||||
|
import android.window.BackEvent
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class TouchTrackerTest {
|
||||||
|
private fun linearTouchTracker(): TouchTracker = TouchTracker().apply {
|
||||||
|
setProgressThresholds(MAX_DISTANCE, MAX_DISTANCE, NON_LINEAR_FACTOR)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun nonLinearTouchTracker(): TouchTracker = TouchTracker().apply {
|
||||||
|
setProgressThresholds(LINEAR_DISTANCE, MAX_DISTANCE, NON_LINEAR_FACTOR)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun TouchTracker.assertProgress(expected: Float) {
|
||||||
|
val actualProgress = createProgressEvent().progress
|
||||||
|
assertEquals(expected, actualProgress, /* delta = */ 0f)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun generatesProgress_onStart() {
|
||||||
|
val linearTracker = linearTouchTracker()
|
||||||
|
linearTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0f, BackEvent.EDGE_LEFT)
|
||||||
|
val event = linearTracker.createStartEvent(null)
|
||||||
|
assertEquals(0f, event.progress, 0f)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun generatesProgress_leftEdge() {
|
||||||
|
val linearTracker = linearTouchTracker()
|
||||||
|
linearTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0f, BackEvent.EDGE_LEFT)
|
||||||
|
var touchX = 10f
|
||||||
|
val velocityX = 0f
|
||||||
|
val velocityY = 0f
|
||||||
|
|
||||||
|
// Pre-commit
|
||||||
|
linearTracker.update(touchX, 0f, velocityX, velocityY)
|
||||||
|
linearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / MAX_DISTANCE)
|
||||||
|
|
||||||
|
// Post-commit
|
||||||
|
touchX += 100f
|
||||||
|
linearTracker.setTriggerBack(true)
|
||||||
|
linearTracker.update(touchX, 0f, velocityX, velocityY)
|
||||||
|
linearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / MAX_DISTANCE)
|
||||||
|
|
||||||
|
// Cancel
|
||||||
|
touchX -= 10f
|
||||||
|
linearTracker.setTriggerBack(false)
|
||||||
|
linearTracker.update(touchX, 0f, velocityX, velocityY)
|
||||||
|
linearTracker.assertProgress(0f)
|
||||||
|
|
||||||
|
// Cancel more
|
||||||
|
touchX -= 10f
|
||||||
|
linearTracker.update(touchX, 0f, velocityX, velocityY)
|
||||||
|
linearTracker.assertProgress(0f)
|
||||||
|
|
||||||
|
// Restart
|
||||||
|
touchX += 10f
|
||||||
|
linearTracker.update(touchX, 0f, velocityX, velocityY)
|
||||||
|
linearTracker.assertProgress(0f)
|
||||||
|
|
||||||
|
// Restarted, but pre-commit
|
||||||
|
val restartX = touchX
|
||||||
|
touchX += 10f
|
||||||
|
linearTracker.update(touchX, 0f, velocityX, velocityY)
|
||||||
|
linearTracker.assertProgress((touchX - restartX) / MAX_DISTANCE)
|
||||||
|
|
||||||
|
// Restarted, post-commit
|
||||||
|
touchX += 10f
|
||||||
|
linearTracker.setTriggerBack(true)
|
||||||
|
linearTracker.update(touchX, 0f, velocityX, velocityY)
|
||||||
|
linearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / MAX_DISTANCE)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun generatesProgress_rightEdge() {
|
||||||
|
val linearTracker = linearTouchTracker()
|
||||||
|
linearTracker.setGestureStartLocation(INITIAL_X_RIGHT_EDGE, 0f, BackEvent.EDGE_RIGHT)
|
||||||
|
var touchX = INITIAL_X_RIGHT_EDGE - 10 // Fake right edge
|
||||||
|
val velocityX = 0f
|
||||||
|
val velocityY = 0f
|
||||||
|
val target = MAX_DISTANCE
|
||||||
|
|
||||||
|
// Pre-commit
|
||||||
|
linearTracker.update(touchX, 0f, velocityX, velocityY)
|
||||||
|
linearTracker.assertProgress((INITIAL_X_RIGHT_EDGE - touchX) / target)
|
||||||
|
|
||||||
|
// Post-commit
|
||||||
|
touchX -= 100f
|
||||||
|
linearTracker.setTriggerBack(true)
|
||||||
|
linearTracker.update(touchX, 0f, velocityX, velocityY)
|
||||||
|
linearTracker.assertProgress((INITIAL_X_RIGHT_EDGE - touchX) / target)
|
||||||
|
|
||||||
|
// Cancel
|
||||||
|
touchX += 10f
|
||||||
|
linearTracker.setTriggerBack(false)
|
||||||
|
linearTracker.update(touchX, 0f, velocityX, velocityY)
|
||||||
|
linearTracker.assertProgress(0f)
|
||||||
|
|
||||||
|
// Cancel more
|
||||||
|
touchX += 10f
|
||||||
|
linearTracker.update(touchX, 0f, velocityX, velocityY)
|
||||||
|
linearTracker.assertProgress(0f)
|
||||||
|
|
||||||
|
// Restart
|
||||||
|
touchX -= 10f
|
||||||
|
linearTracker.update(touchX, 0f, velocityX, velocityY)
|
||||||
|
linearTracker.assertProgress(0f)
|
||||||
|
|
||||||
|
// Restarted, but pre-commit
|
||||||
|
val restartX = touchX
|
||||||
|
touchX -= 10f
|
||||||
|
linearTracker.update(touchX, 0f, velocityX, velocityY)
|
||||||
|
linearTracker.assertProgress((restartX - touchX) / target)
|
||||||
|
|
||||||
|
// Restarted, post-commit
|
||||||
|
touchX -= 10f
|
||||||
|
linearTracker.setTriggerBack(true)
|
||||||
|
linearTracker.update(touchX, 0f, velocityX, velocityY)
|
||||||
|
linearTracker.assertProgress((INITIAL_X_RIGHT_EDGE - touchX) / target)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun generatesNonLinearProgress_leftEdge() {
|
||||||
|
val nonLinearTracker = nonLinearTouchTracker()
|
||||||
|
nonLinearTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0f, BackEvent.EDGE_LEFT)
|
||||||
|
var touchX = 10f
|
||||||
|
val velocityX = 0f
|
||||||
|
val velocityY = 0f
|
||||||
|
val linearTarget = LINEAR_DISTANCE + (MAX_DISTANCE - LINEAR_DISTANCE) * NON_LINEAR_FACTOR
|
||||||
|
|
||||||
|
// Pre-commit: linear progress
|
||||||
|
nonLinearTracker.update(touchX, 0f, velocityX, velocityY)
|
||||||
|
nonLinearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / linearTarget)
|
||||||
|
|
||||||
|
// Post-commit: still linear progress
|
||||||
|
touchX += 100f
|
||||||
|
nonLinearTracker.setTriggerBack(true)
|
||||||
|
nonLinearTracker.update(touchX, 0f, velocityX, velocityY)
|
||||||
|
nonLinearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / linearTarget)
|
||||||
|
|
||||||
|
// still linear progress
|
||||||
|
touchX = INITIAL_X_LEFT_EDGE + LINEAR_DISTANCE
|
||||||
|
nonLinearTracker.update(touchX, 0f, velocityX, velocityY)
|
||||||
|
nonLinearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / linearTarget)
|
||||||
|
|
||||||
|
// non linear progress
|
||||||
|
touchX += 10
|
||||||
|
nonLinearTracker.update(touchX, 0f, velocityX, velocityY)
|
||||||
|
val nonLinearTouch = (touchX - INITIAL_X_LEFT_EDGE) - LINEAR_DISTANCE
|
||||||
|
val nonLinearProgress = nonLinearTouch / NON_LINEAR_DISTANCE
|
||||||
|
val nonLinearTarget = MathUtils.lerp(linearTarget, MAX_DISTANCE, nonLinearProgress)
|
||||||
|
nonLinearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / nonLinearTarget)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val MAX_DISTANCE = 500f
|
||||||
|
private const val LINEAR_DISTANCE = 400f
|
||||||
|
private const val NON_LINEAR_DISTANCE = MAX_DISTANCE - LINEAR_DISTANCE
|
||||||
|
private const val NON_LINEAR_FACTOR = 0.2f
|
||||||
|
private const val INITIAL_X_LEFT_EDGE = 5f
|
||||||
|
private const val INITIAL_X_RIGHT_EDGE = MAX_DISTANCE - INITIAL_X_LEFT_EDGE
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -45,6 +45,9 @@
|
|||||||
<dimen name="navigation_edge_action_drag_threshold">16dp</dimen>
|
<dimen name="navigation_edge_action_drag_threshold">16dp</dimen>
|
||||||
<!-- The threshold to progress back animation for edge swipe -->
|
<!-- The threshold to progress back animation for edge swipe -->
|
||||||
<dimen name="navigation_edge_action_progress_threshold">412dp</dimen>
|
<dimen name="navigation_edge_action_progress_threshold">412dp</dimen>
|
||||||
|
<!-- This value is used to calculate the target if the screen is wider than the
|
||||||
|
navigation_edge_action_progress_threshold. See BackAnimation#setSwipeThresholds -->
|
||||||
|
<item name="back_progress_non_linear_factor" format="float" type="dimen">0.2</item>
|
||||||
<!-- The minimum display position of the arrow on the screen -->
|
<!-- The minimum display position of the arrow on the screen -->
|
||||||
<dimen name="navigation_edge_arrow_min_y">64dp</dimen>
|
<dimen name="navigation_edge_arrow_min_y">64dp</dimen>
|
||||||
<!-- The amount by which the arrow is shifted to avoid the finger-->
|
<!-- The amount by which the arrow is shifted to avoid the finger-->
|
||||||
|
|||||||
@@ -63,6 +63,8 @@ import android.view.WindowInsets;
|
|||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.window.BackEvent;
|
import android.window.BackEvent;
|
||||||
|
|
||||||
|
import androidx.annotation.DimenRes;
|
||||||
|
|
||||||
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
|
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
|
||||||
import com.android.internal.policy.GestureNavigationSettingsObserver;
|
import com.android.internal.policy.GestureNavigationSettingsObserver;
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
@@ -225,7 +227,8 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
|
|||||||
// The slop to distinguish between horizontal and vertical motion
|
// The slop to distinguish between horizontal and vertical motion
|
||||||
private float mTouchSlop;
|
private float mTouchSlop;
|
||||||
// The threshold for back swipe full progress.
|
// The threshold for back swipe full progress.
|
||||||
private float mBackSwipeProgressThreshold;
|
private float mBackSwipeLinearThreshold;
|
||||||
|
private float mNonLinearFactor;
|
||||||
// Duration after which we consider the event as longpress.
|
// Duration after which we consider the event as longpress.
|
||||||
private final int mLongPressTimeout;
|
private final int mLongPressTimeout;
|
||||||
private int mStartingQuickstepRotation = -1;
|
private int mStartingQuickstepRotation = -1;
|
||||||
@@ -471,11 +474,19 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
|
|||||||
final float backGestureSlop = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI,
|
final float backGestureSlop = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||||
SystemUiDeviceConfigFlags.BACK_GESTURE_SLOP_MULTIPLIER, 0.75f);
|
SystemUiDeviceConfigFlags.BACK_GESTURE_SLOP_MULTIPLIER, 0.75f);
|
||||||
mTouchSlop = mViewConfiguration.getScaledTouchSlop() * backGestureSlop;
|
mTouchSlop = mViewConfiguration.getScaledTouchSlop() * backGestureSlop;
|
||||||
mBackSwipeProgressThreshold = res.getDimension(
|
mBackSwipeLinearThreshold = res.getDimension(
|
||||||
R.dimen.navigation_edge_action_progress_threshold);
|
R.dimen.navigation_edge_action_progress_threshold);
|
||||||
|
mNonLinearFactor = getDimenFloat(res,
|
||||||
|
R.dimen.back_progress_non_linear_factor);
|
||||||
updateBackAnimationThresholds();
|
updateBackAnimationThresholds();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private float getDimenFloat(Resources res, @DimenRes int resId) {
|
||||||
|
TypedValue typedValue = new TypedValue();
|
||||||
|
res.getValue(resId, typedValue, true);
|
||||||
|
return typedValue.getFloat();
|
||||||
|
}
|
||||||
|
|
||||||
public void updateNavigationBarOverlayExcludeRegion(Rect exclude) {
|
public void updateNavigationBarOverlayExcludeRegion(Rect exclude) {
|
||||||
mNavBarOverlayExcludedBounds.set(exclude);
|
mNavBarOverlayExcludedBounds.set(exclude);
|
||||||
}
|
}
|
||||||
@@ -1116,8 +1127,9 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
|
|||||||
if (mBackAnimation == null) {
|
if (mBackAnimation == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mBackAnimation.setSwipeThresholds(
|
int maxDistance = mDisplaySize.x;
|
||||||
Math.min(mDisplaySize.x, mBackSwipeProgressThreshold));
|
float linearDistance = Math.min(maxDistance, mBackSwipeLinearThreshold);
|
||||||
|
mBackAnimation.setSwipeThresholds(linearDistance, maxDistance, mNonLinearFactor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean sendEvent(int action, int code) {
|
private boolean sendEvent(int action, int code) {
|
||||||
|
|||||||
Reference in New Issue
Block a user