Use movement of the motion events on the screen instead of on the

trackpad since they are proportional

This simplifies the logic we have to track the movements in the code.
Also a slight fix to not cancel back gesture on pointer down from trackpad

Bug: 255697805
Test: 3 finger swipe to go back with the flag turned on
Change-Id: Ie85ad1d61f846ece8e515ddd456bd1729a45354e
This commit is contained in:
Tracy Zhou
2023-02-07 06:29:02 +00:00
parent 9222c80306
commit 99ff985abc
8 changed files with 17 additions and 326 deletions

View File

@@ -1,31 +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.systemui.plugins;
import android.view.MotionEvent;
/** Handles both trackpad and touch events and report displacements in both axis's. */
public interface MotionEventsHandlerBase {
void onMotionEvent(MotionEvent ev);
float getDisplacementX(MotionEvent ev);
float getDisplacementY(MotionEvent ev);
String dump();
}

View File

@@ -48,9 +48,6 @@ public interface NavigationEdgeBackPlugin extends Plugin {
/** Sets the base LayoutParams for the UI. */
void setLayoutParams(WindowManager.LayoutParams layoutParams);
/** Sets the motion events handler for the plugin. */
default void setMotionEventsHandler(MotionEventsHandlerBase motionEventsHandler) {}
/** Updates the UI based on the motion events passed in device coordinates. */
void onMotionEvent(MotionEvent motionEvent);

View File

@@ -36,7 +36,6 @@ import androidx.core.view.isVisible
import androidx.dynamicanimation.animation.DynamicAnimation
import com.android.internal.util.LatencyTracker
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.MotionEventsHandlerBase
import com.android.systemui.plugins.NavigationEdgeBackPlugin
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -592,10 +591,6 @@ class BackPanelController internal constructor(
windowManager.addView(mView, layoutParams)
}
override fun setMotionEventsHandler(motionEventsHandler: MotionEventsHandlerBase?) {
// TODO(255697805): Integrate MotionEventHandler for trackpad.
}
private fun isDragAwayFromEdge(velocityPxPerSecThreshold: Int = 0) = velocityTracker!!.run {
computeCurrentVelocity(PX_PER_SEC)
val velocity = xVelocity.takeIf { mView.isLeftPanel } ?: (xVelocity * -1)
@@ -1048,4 +1043,4 @@ class Step<T>(
else -> startValue
}.also { previousValue = it }
}
}
}

View File

@@ -18,7 +18,6 @@ package com.android.systemui.navigationbar.gestural;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
import static com.android.systemui.classifier.Classifier.BACK_GESTURE;
import static com.android.systemui.navigationbar.gestural.Utilities.getTrackpadScale;
import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadMotionEvent;
import android.annotation.NonNull;
@@ -272,7 +271,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
private LogArray mGestureLogOutsideInsets = new LogArray(MAX_NUM_LOGGED_GESTURES);
private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver;
private final MotionEventsHandler mMotionEventsHandler;
private final NavigationEdgeBackPlugin.BackCallback mBackCallback =
new NavigationEdgeBackPlugin.BackCallback() {
@@ -404,7 +402,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
mGestureNavigationSettingsObserver = new GestureNavigationSettingsObserver(
mContext.getMainThreadHandler(), mContext, this::onNavigationSettingsChanged);
mMotionEventsHandler = new MotionEventsHandler(featureFlags, getTrackpadScale(context));
updateCurrentUserResources();
}
@@ -624,7 +621,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
Trace.beginSection("setEdgeBackPlugin");
mEdgeBackPlugin = edgeBackPlugin;
mEdgeBackPlugin.setBackCallback(mBackCallback);
mEdgeBackPlugin.setMotionEventsHandler(mMotionEventsHandler);
mEdgeBackPlugin.setLayoutParams(createLayoutParams());
updateDisplaySize();
} finally {
@@ -871,7 +867,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
}
private void onMotionEvent(MotionEvent ev) {
mMotionEventsHandler.onMotionEvent(ev);
boolean isTrackpadEvent = isTrackpadMotionEvent(mIsTrackpadGestureBackEnabled, ev);
int action = ev.getActionMasked();
if (action == MotionEvent.ACTION_DOWN) {
if (DEBUG_MISSING_GESTURE) {
@@ -881,7 +877,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
// Verify if this is in within the touch region and we aren't in immersive mode, and
// either the bouncer is showing or the notification panel is hidden
mInputEventReceiver.setBatchingEnabled(false);
boolean isTrackpadEvent = isTrackpadMotionEvent(mIsTrackpadGestureBackEnabled, ev);
if (isTrackpadEvent) {
// TODO: show the back arrow based on the direction of the swipe.
mIsOnLeftEdge = false;
@@ -921,7 +916,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
if (!mThresholdCrossed) {
mEndPoint.x = (int) ev.getX();
mEndPoint.y = (int) ev.getY();
if (action == MotionEvent.ACTION_POINTER_DOWN) {
if (action == MotionEvent.ACTION_POINTER_DOWN && !isTrackpadEvent) {
if (mAllowGesture) {
logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_MULTI_TOUCH);
if (DEBUG_MISSING_GESTURE) {
@@ -947,8 +942,8 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
mLogGesture = false;
return;
}
float dx = Math.abs(mMotionEventsHandler.getDisplacementX(ev));
float dy = Math.abs(mMotionEventsHandler.getDisplacementY(ev));
float dx = Math.abs(ev.getX() - mDownPoint.x);
float dy = Math.abs(ev.getY() - mDownPoint.y);
if (dy > dx && dy > mTouchSlop) {
if (mAllowGesture) {
logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_VERTICAL_MOVE);
@@ -1086,7 +1081,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
pw.println(" mGestureLogInsideInsets=" + String.join("\n", mGestureLogInsideInsets));
pw.println(" mGestureLogOutsideInsets=" + String.join("\n", mGestureLogOutsideInsets));
pw.println(" mEdgeBackPlugin=" + mEdgeBackPlugin);
pw.println(" mMotionEventsHandler=" + mMotionEventsHandler);
if (mEdgeBackPlugin != null) {
mEdgeBackPlugin.dump(pw);
}

View File

@@ -1,114 +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.systemui.navigationbar.gestural;
import static android.view.MotionEvent.AXIS_GESTURE_X_OFFSET;
import static android.view.MotionEvent.AXIS_GESTURE_Y_OFFSET;
import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadMotionEvent;
import android.graphics.PointF;
import android.view.MotionEvent;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.MotionEventsHandlerBase;
/** Handles both trackpad and touch events and report displacements in both axis's. */
public class MotionEventsHandler implements MotionEventsHandlerBase {
private final boolean mIsTrackpadGestureBackEnabled;
private final int mScale;
private final PointF mDownPos = new PointF();
private final PointF mLastPos = new PointF();
private float mCurrentTrackpadOffsetX = 0;
private float mCurrentTrackpadOffsetY = 0;
public MotionEventsHandler(FeatureFlags featureFlags, int scale) {
mIsTrackpadGestureBackEnabled = featureFlags.isEnabled(Flags.TRACKPAD_GESTURE_BACK);
mScale = scale;
}
@Override
public void onMotionEvent(MotionEvent ev) {
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
onActionDown(ev);
break;
case MotionEvent.ACTION_MOVE:
onActionMove(ev);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
onActionUp(ev);
break;
default:
break;
}
}
private void onActionDown(MotionEvent ev) {
reset();
if (!isTrackpadMotionEvent(mIsTrackpadGestureBackEnabled, ev)) {
mDownPos.set(ev.getX(), ev.getY());
mLastPos.set(mDownPos);
}
}
private void onActionMove(MotionEvent ev) {
updateMovements(ev);
}
private void onActionUp(MotionEvent ev) {
updateMovements(ev);
}
private void updateMovements(MotionEvent ev) {
if (isTrackpadMotionEvent(mIsTrackpadGestureBackEnabled, ev)) {
mCurrentTrackpadOffsetX += ev.getAxisValue(AXIS_GESTURE_X_OFFSET) * mScale;
mCurrentTrackpadOffsetY += ev.getAxisValue(AXIS_GESTURE_Y_OFFSET) * mScale;
} else {
mLastPos.set(ev.getX(), ev.getY());
}
}
private void reset() {
mDownPos.set(0, 0);
mLastPos.set(0, 0);
mCurrentTrackpadOffsetX = 0;
mCurrentTrackpadOffsetY = 0;
}
@Override
public float getDisplacementX(MotionEvent ev) {
return isTrackpadMotionEvent(mIsTrackpadGestureBackEnabled, ev) ? mCurrentTrackpadOffsetX
: mLastPos.x - mDownPos.x;
}
@Override
public float getDisplacementY(MotionEvent ev) {
return isTrackpadMotionEvent(mIsTrackpadGestureBackEnabled, ev) ? mCurrentTrackpadOffsetY
: mLastPos.y - mDownPos.y;
}
@Override
public String dump() {
return "mDownPos: " + mDownPos + ", mLastPos: " + mLastPos + ", mCurrentTrackpadOffsetX: "
+ mCurrentTrackpadOffsetX + ", mCurrentTrackpadOffsetY: " + mCurrentTrackpadOffsetY;
}
}

View File

@@ -53,7 +53,6 @@ import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.plugins.MotionEventsHandlerBase;
import com.android.systemui.plugins.NavigationEdgeBackPlugin;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
@@ -200,6 +199,8 @@ public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPl
*/
private boolean mIsLeftPanel;
private float mStartX;
private float mStartY;
private float mCurrentAngle;
/**
* The current translation of the arrow
@@ -230,8 +231,6 @@ public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPl
private final Handler mHandler = new Handler();
private final Runnable mFailsafeRunnable = this::onFailsafe;
private MotionEventsHandlerBase mMotionEventsHandler;
private DynamicAnimation.OnAnimationEndListener mSetGoneEndListener
= new DynamicAnimation.OnAnimationEndListener() {
@Override
@@ -438,11 +437,6 @@ public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPl
mWindowManager.addView(this, mLayoutParams);
}
@Override
public void setMotionEventsHandler(MotionEventsHandlerBase motionEventsHandler) {
mMotionEventsHandler = motionEventsHandler;
}
/**
* Adjusts the sampling rect to conform to the actual visible bounding box of the arrow.
*/
@@ -487,6 +481,8 @@ public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPl
case MotionEvent.ACTION_DOWN:
mDragSlopPassed = false;
resetOnDown();
mStartX = event.getX();
mStartY = event.getY();
setVisibility(VISIBLE);
updatePosition(event.getY());
mRegionSamplingHelper.start(mSamplingRect);
@@ -730,9 +726,10 @@ public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPl
}
private void handleMoveEvent(MotionEvent event) {
float xOffset = mMotionEventsHandler.getDisplacementX(event);
float touchTranslation = MathUtils.abs(xOffset);
float yOffset = mMotionEventsHandler.getDisplacementY(event);
float x = event.getX();
float y = event.getY();
float touchTranslation = MathUtils.abs(x - mStartX);
float yOffset = y - mStartY;
float delta = touchTranslation - mPreviousTouchTranslation;
if (Math.abs(delta) > 0) {
if (Math.signum(delta) == Math.signum(mTotalTouchDelta)) {
@@ -793,14 +790,16 @@ public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPl
}
// Last if the direction in Y is bigger than X * 2 we also abort
if (Math.abs(yOffset) > Math.abs(xOffset) * 2) {
if (Math.abs(yOffset) > Math.abs(x - mStartX) * 2) {
triggerBack = false;
}
if (DEBUG_MISSING_GESTURE && mTriggerBack != triggerBack) {
Log.d(DEBUG_MISSING_GESTURE_TAG, "set mTriggerBack=" + triggerBack
+ ", mTotalTouchDelta=" + mTotalTouchDelta
+ ", mMinDeltaForSwitch=" + mMinDeltaForSwitch
+ ", yOffset=" + yOffset + mMotionEventsHandler.dump());
+ ", yOffset=" + yOffset
+ ", x=" + x
+ ", mStartX=" + mStartX);
}
setTriggerBack(triggerBack, true /* animated */);

View File

@@ -18,21 +18,13 @@ package com.android.systemui.navigationbar.gestural;
import static android.view.MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE;
import android.content.Context;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
public final class Utilities {
private static final int TRACKPAD_GESTURE_SCALE = 200;
public static boolean isTrackpadMotionEvent(boolean isTrackpadGestureBackEnabled,
MotionEvent event) {
return isTrackpadGestureBackEnabled
&& event.getClassification() == CLASSIFICATION_MULTI_FINGER_SWIPE;
}
public static int getTrackpadScale(Context context) {
return ViewConfiguration.get(context).getScaledTouchSlop() * TRACKPAD_GESTURE_SCALE;
}
}

View File

@@ -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.systemui.navigationbar.gestural;
import static android.view.InputDevice.SOURCE_MOUSE;
import static android.view.MotionEvent.AXIS_GESTURE_X_OFFSET;
import static android.view.MotionEvent.AXIS_GESTURE_Y_OFFSET;
import static android.view.MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE;
import static com.google.common.truth.Truth.assertThat;
import android.view.MotionEvent;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.Flags;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Tests for {@link MotionEventsHandler}.
*/
@RunWith(AndroidJUnit4.class)
@SmallTest
public class MotionEventsHandlerTest extends SysuiTestCase {
private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
private static int SCALE = 100;
private MotionEventsHandler mMotionEventsHandler;
@Before
public void setUp() {
mFeatureFlags.set(Flags.TRACKPAD_GESTURE_BACK, true);
mMotionEventsHandler = new MotionEventsHandler(mFeatureFlags, SCALE);
}
@Test
public void onTouchEvent_touchScreen_hasCorrectDisplacements() {
MotionEvent down = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 100, 100, 0);
MotionEvent move1 = MotionEvent.obtain(0, 1, MotionEvent.ACTION_MOVE, 150, 125, 0);
MotionEvent move2 = MotionEvent.obtain(0, 2, MotionEvent.ACTION_MOVE, 200, 150, 0);
MotionEvent up = MotionEvent.obtain(0, 3, MotionEvent.ACTION_UP, 250, 175, 0);
mMotionEventsHandler.onMotionEvent(down);
mMotionEventsHandler.onMotionEvent(move1);
assertThat(mMotionEventsHandler.getDisplacementX(move1)).isEqualTo(50);
assertThat(mMotionEventsHandler.getDisplacementY(move1)).isEqualTo(25);
mMotionEventsHandler.onMotionEvent(move2);
assertThat(mMotionEventsHandler.getDisplacementX(move2)).isEqualTo(100);
assertThat(mMotionEventsHandler.getDisplacementY(move2)).isEqualTo(50);
mMotionEventsHandler.onMotionEvent(up);
assertThat(mMotionEventsHandler.getDisplacementX(up)).isEqualTo(150);
assertThat(mMotionEventsHandler.getDisplacementY(up)).isEqualTo(75);
}
@Test
public void onTouchEvent_trackpad_hasCorrectDisplacements() {
MotionEvent.PointerCoords[] downPointerCoords = new MotionEvent.PointerCoords[1];
downPointerCoords[0] = new MotionEvent.PointerCoords();
downPointerCoords[0].setAxisValue(AXIS_GESTURE_X_OFFSET, 0.1f);
downPointerCoords[0].setAxisValue(AXIS_GESTURE_Y_OFFSET, 0.1f);
MotionEvent.PointerProperties[] downPointerProperties =
new MotionEvent.PointerProperties[1];
downPointerProperties[0] = new MotionEvent.PointerProperties();
downPointerProperties[0].id = 1;
downPointerProperties[0].toolType = MotionEvent.TOOL_TYPE_FINGER;
MotionEvent down = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 1,
downPointerProperties, downPointerCoords, 0, 0, 1.0f, 1.0f, 0, 0, SOURCE_MOUSE,
0, 0, CLASSIFICATION_MULTI_FINGER_SWIPE);
MotionEvent.PointerCoords[] movePointerCoords1 = new MotionEvent.PointerCoords[1];
movePointerCoords1[0] = new MotionEvent.PointerCoords();
movePointerCoords1[0].setAxisValue(AXIS_GESTURE_X_OFFSET, 0.2f);
movePointerCoords1[0].setAxisValue(AXIS_GESTURE_Y_OFFSET, 0.1f);
MotionEvent.PointerProperties[] movePointerProperties1 =
new MotionEvent.PointerProperties[1];
movePointerProperties1[0] = new MotionEvent.PointerProperties();
movePointerProperties1[0].id = 1;
movePointerProperties1[0].toolType = MotionEvent.TOOL_TYPE_FINGER;
MotionEvent move1 = MotionEvent.obtain(0, 1, MotionEvent.ACTION_MOVE, 1,
movePointerProperties1, movePointerCoords1, 0, 0, 1.0f, 1.0f, 0, 0, SOURCE_MOUSE,
0, 0, CLASSIFICATION_MULTI_FINGER_SWIPE);
MotionEvent.PointerCoords[] movePointerCoords2 = new MotionEvent.PointerCoords[1];
movePointerCoords2[0] = new MotionEvent.PointerCoords();
movePointerCoords2[0].setAxisValue(AXIS_GESTURE_X_OFFSET, 0.1f);
movePointerCoords2[0].setAxisValue(AXIS_GESTURE_Y_OFFSET, 0.4f);
MotionEvent.PointerProperties[] movePointerProperties2 =
new MotionEvent.PointerProperties[1];
movePointerProperties2[0] = new MotionEvent.PointerProperties();
movePointerProperties2[0].id = 1;
movePointerProperties2[0].toolType = MotionEvent.TOOL_TYPE_FINGER;
MotionEvent move2 = MotionEvent.obtain(0, 2, MotionEvent.ACTION_MOVE, 1,
movePointerProperties2, movePointerCoords2, 0, 0, 1.0f, 1.0f, 0, 0, SOURCE_MOUSE,
0, 0, CLASSIFICATION_MULTI_FINGER_SWIPE);
MotionEvent.PointerCoords[] upPointerCoords = new MotionEvent.PointerCoords[1];
upPointerCoords[0] = new MotionEvent.PointerCoords();
upPointerCoords[0].setAxisValue(AXIS_GESTURE_X_OFFSET, 0.1f);
upPointerCoords[0].setAxisValue(AXIS_GESTURE_Y_OFFSET, 0.1f);
MotionEvent.PointerProperties[] upPointerProperties2 =
new MotionEvent.PointerProperties[1];
upPointerProperties2[0] = new MotionEvent.PointerProperties();
upPointerProperties2[0].id = 1;
upPointerProperties2[0].toolType = MotionEvent.TOOL_TYPE_FINGER;
MotionEvent up = MotionEvent.obtain(0, 2, MotionEvent.ACTION_UP, 1,
upPointerProperties2, upPointerCoords, 0, 0, 1.0f, 1.0f, 0, 0, SOURCE_MOUSE,
0, 0, CLASSIFICATION_MULTI_FINGER_SWIPE);
mMotionEventsHandler.onMotionEvent(down);
mMotionEventsHandler.onMotionEvent(move1);
assertThat(mMotionEventsHandler.getDisplacementX(move1)).isEqualTo(20f);
assertThat(mMotionEventsHandler.getDisplacementY(move1)).isEqualTo(10f);
mMotionEventsHandler.onMotionEvent(move2);
assertThat(mMotionEventsHandler.getDisplacementX(move2)).isEqualTo(30f);
assertThat(mMotionEventsHandler.getDisplacementY(move2)).isEqualTo(50f);
mMotionEventsHandler.onMotionEvent(up);
assertThat(mMotionEventsHandler.getDisplacementX(up)).isEqualTo(40f);
assertThat(mMotionEventsHandler.getDisplacementY(up)).isEqualTo(60f);
}
}