Base: Predictive Back, add support for large screens when swiping
The back progress is linear on smartphones or devices with a screen width less than the `linearDistance`, which can be configured in the resources. The `deltaX` is the distance from the current point to the point where the back started.
The progress value is calculated as follows:
- If the `maxDistance` (screen width) is less than the `linearDistance`, the progress will be completely linear (with target `maxDistance`), so we will have: `progress = deltaX / maxDistance`
- If the `maxDistance` is greater than the `linearDistance`, the progress will be linear for the first part (up to `linearDistance`) and then go slowly to 1f.
- If `deltaX` is less than `linearDistance`, we are in the linear part, and the target will no longer be the `maxDistance` but the sum of `linearDistance` and `nonLinearDistance * nonLinearFactor` (a distance between `linearDistance` and `maxDistance`).
- If `deltaX` is greater than `linearDistance`, we are in the non-linear part, and the target will move linearly towards `maxDistance` so as to have a smooth curve towards the end of the screen.
The resources used are:
- `linearDistance` uses the `resource navigation_edge_action_progress_threshold` (with value 412dp)
- `nonLinearFactor` uses the `resource navigation_edge_action_progress_non_linear_factor` (with value 0.2)
screen screen screen
width width width
|——————| |————————————| |————————————————————|
A B A B C A
1 +——————+—————+ 1 +————————————+ 1 +————————————+———————+
| / | | —/| | | —————/|
| / | | —/ | | ——/ |
| / | | —/ | | ——/ | |
| / | | —/ | | ——/ | |
| / | | —/ | | ——/ | |
|/ | |—/ | |—/ | |
0 +————————————+ 0 +————————————+ 0 +————————————+———————+
B B B
Three devices with different widths (smaller, equal, and wider) relative to the progress
threshold are shown in the graphs.
- `A` is the width of the screen
- `B` is the progress threshold (horizontal swipe distance where progress is linear)
- `C` equals `B + (A - B) * nonLinearFactor`
If `A` is less than or equal to `B`, `progress` for the swipe distance between:
- `[0, A]` will scale linearly between `[0, 1]`.
If A is greater than B, `progress` for swipe distance between:
- `[0, B]` will scale linearly between `[0, B / C]`
- `(B, A]` will scale non-linearly and reach 1.
Test: atest TouchTrackerTest
Bug: 271508045
Change-Id: Ib96fea37ba1af091cbe3406754f3a35ccf5557f7
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
index 53a438e..c980906 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
@@ -54,10 +54,42 @@
void setTriggerBack(boolean triggerBack);
/**
- * Sets the threshold values that defining edge swipe behavior.
- * @param progressThreshold the max threshold to keep linear progressing back animation.
+ * Sets the threshold values that define edge swipe behavior.<br>
+ * <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.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 6d879b8..bb543f2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -301,9 +301,12 @@
}
@Override
- public void setSwipeThresholds(float progressThreshold) {
+ public void setSwipeThresholds(
+ float linearDistance,
+ float maxDistance,
+ float nonLinearFactor) {
mShellExecutor.execute(() -> BackAnimationController.this.setSwipeThresholds(
- progressThreshold));
+ linearDistance, maxDistance, nonLinearFactor));
}
@Override
@@ -509,7 +512,7 @@
// Constraints - absolute values
float minVelocity = mFlingAnimationUtils.getMinVelocityPxPerSecond();
float maxVelocity = mFlingAnimationUtils.getHighVelocityPxPerSecond();
- float maxX = mTouchTracker.getMaxX(); // px
+ float maxX = mTouchTracker.getMaxDistance(); // px
float maxFlingDistance = maxX * MAX_FLING_PROGRESS; // px
// Current state
@@ -605,8 +608,11 @@
mTouchTracker.setTriggerBack(triggerBack);
}
- private void setSwipeThresholds(float progressThreshold) {
- mTouchTracker.setProgressThreshold(progressThreshold);
+ private void setSwipeThresholds(
+ float linearDistance,
+ float maxDistance,
+ float nonLinearFactor) {
+ mTouchTracker.setProgressThresholds(linearDistance, maxDistance, nonLinearFactor);
}
private void invokeOrCancelBack() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
index 7a00f5b..a0ada39 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
@@ -28,11 +28,13 @@
* Helper class to record the touch location for gesture and generate back events.
*/
class TouchTracker {
- private static final String PREDICTIVE_BACK_PROGRESS_THRESHOLD_PROP =
- "persist.wm.debug.predictive_back_progress_threshold";
- private static final int PROGRESS_THRESHOLD = SystemProperties
- .getInt(PREDICTIVE_BACK_PROGRESS_THRESHOLD_PROP, -1);
- private float mProgressThreshold;
+ 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
*/
@@ -125,17 +127,42 @@
// the location everytime back is restarted after being cancelled.
float startX = mTriggerBack ? mInitTouchX : mStartThresholdX;
float deltaX = Math.abs(startX - touchX);
- float maxX = getMaxX();
- maxX = maxX == 0 ? 1 : maxX;
- return MathUtils.constrain(deltaX / maxX, 0, 1);
+ 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 X value (in pixels).
+ * Maximum distance in pixels.
* Progress is considered to be completed (1f) when this limit is exceeded.
*/
- float getMaxX() {
- return PROGRESS_THRESHOLD >= 0 ? PROGRESS_THRESHOLD : mProgressThreshold;
+ float getMaxDistance() {
+ return mMaxDistance;
}
BackMotionEvent createProgressEvent(float progress) {
@@ -149,7 +176,14 @@
/* departingAnimationTarget = */ null);
}
- public void setProgressThreshold(float progressThreshold) {
- mProgressThreshold = progressThreshold;
+ public void setProgressThresholds(float linearDistance, float maxDistance,
+ float nonLinearFactor) {
+ if (LINEAR_DISTANCE >= 0) {
+ mLinearDistance = LINEAR_DISTANCE;
+ } else {
+ mLinearDistance = linearDistance;
+ }
+ mMaxDistance = maxDistance;
+ mNonLinearFactor = nonLinearFactor;
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.java
deleted file mode 100644
index d62e660..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.java
+++ /dev/null
@@ -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();
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt
new file mode 100644
index 0000000..9088e89
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt
@@ -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
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index aff0e80..ca4b3a2 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -45,6 +45,9 @@
<dimen name="navigation_edge_action_drag_threshold">16dp</dimen>
<!-- The threshold to progress back animation for edge swipe -->
<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 -->
<dimen name="navigation_edge_arrow_min_y">64dp</dimen>
<!-- The amount by which the arrow is shifted to avoid the finger-->
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 42de7f0..5818fd0 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -63,6 +63,8 @@
import android.view.WindowManager;
import android.window.BackEvent;
+import androidx.annotation.DimenRes;
+
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.policy.GestureNavigationSettingsObserver;
import com.android.systemui.R;
@@ -225,7 +227,8 @@
// The slop to distinguish between horizontal and vertical motion
private float mTouchSlop;
// 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.
private final int mLongPressTimeout;
private int mStartingQuickstepRotation = -1;
@@ -471,11 +474,19 @@
final float backGestureSlop = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.BACK_GESTURE_SLOP_MULTIPLIER, 0.75f);
mTouchSlop = mViewConfiguration.getScaledTouchSlop() * backGestureSlop;
- mBackSwipeProgressThreshold = res.getDimension(
+ mBackSwipeLinearThreshold = res.getDimension(
R.dimen.navigation_edge_action_progress_threshold);
+ mNonLinearFactor = getDimenFloat(res,
+ R.dimen.back_progress_non_linear_factor);
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) {
mNavBarOverlayExcludedBounds.set(exclude);
}
@@ -1116,8 +1127,9 @@
if (mBackAnimation == null) {
return;
}
- mBackAnimation.setSwipeThresholds(
- Math.min(mDisplaySize.x, mBackSwipeProgressThreshold));
+ int maxDistance = mDisplaySize.x;
+ float linearDistance = Math.min(maxDistance, mBackSwipeLinearThreshold);
+ mBackAnimation.setSwipeThresholds(linearDistance, maxDistance, mNonLinearFactor);
}
private boolean sendEvent(int action, int code) {