Merge changes Id401f273,I0b21c8ea,Ieb7936bd into ub-launcher3-qt-dev
* changes:
Layout aligned to dp grid for portrait (3/3)
Layout aligned to dp grid for portrait (2/3)
Layout aligned to dp grid for portrait (1/3)
diff --git a/Android.mk b/Android.mk
index 6568a26..7956d28 100644
--- a/Android.mk
+++ b/Android.mk
@@ -298,7 +298,7 @@
LOCAL_PACKAGE_NAME := Launcher3GoIconRecents
LOCAL_PRIVILEGED_MODULE := true
LOCAL_PRODUCT_MODULE := true
-LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep Launcher3QuickStepGo
+LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3Go Launcher3QuickStep Launcher3QuickStepGo
LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
LOCAL_FULL_LIBS_MANIFEST_FILES := \
diff --git a/quickstep/recents_ui_overrides/res/values/dimens.xml b/quickstep/recents_ui_overrides/res/values/dimens.xml
index f991435..61c576e 100644
--- a/quickstep/recents_ui_overrides/res/values/dimens.xml
+++ b/quickstep/recents_ui_overrides/res/values/dimens.xml
@@ -21,4 +21,6 @@
<!-- The size of corner radius of the arrow in the arrow toast. -->
<dimen name="arrow_toast_corner_radius">2dp</dimen>
+ <!-- Minimum distance to swipe to trigger accessibility gesture -->
+ <dimen name="accessibility_gesture_min_swipe_distance">80dp</dimen>
</resources>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AccessibilityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AccessibilityInputConsumer.java
new file mode 100644
index 0000000..8f8cd18
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AccessibilityInputConsumer.java
@@ -0,0 +1,156 @@
+/*
+ * 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 android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_POINTER_DOWN;
+import static android.view.MotionEvent.ACTION_POINTER_UP;
+import static android.view.MotionEvent.ACTION_UP;
+
+import android.content.Context;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.Display;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.ViewConfiguration;
+
+import com.android.launcher3.R;
+import com.android.quickstep.util.MotionPauseDetector;
+import com.android.systemui.shared.recents.ISystemUiProxy;
+import com.android.systemui.shared.system.InputMonitorCompat;
+
+/**
+ * Touch consumer for two finger swipe actions for accessibility actions
+ */
+public class AccessibilityInputConsumer extends DelegateInputConsumer {
+
+ private static final String TAG = "A11yInputConsumer";
+
+ private final ISystemUiProxy mSystemUiProxy;
+ private final VelocityTracker mVelocityTracker;
+ private final MotionPauseDetector mMotionPauseDetector;
+ private final boolean mAllowLongClick;
+
+ private final float mMinGestureDistance;
+ private final float mMinFlingVelocity;
+
+ private int mActivePointerId = -1;
+ private float mDownY;
+ private float mTotalY;
+
+ public AccessibilityInputConsumer(Context context, ISystemUiProxy systemUiProxy,
+ boolean allowLongClick, InputConsumer delegate, InputMonitorCompat inputMonitor) {
+ super(delegate, inputMonitor);
+ mSystemUiProxy = systemUiProxy;
+ mVelocityTracker = VelocityTracker.obtain();
+ mMinGestureDistance = context.getResources()
+ .getDimension(R.dimen.accessibility_gesture_min_swipe_distance);
+ mMinFlingVelocity = ViewConfiguration.get(context).getScaledMinimumFlingVelocity();
+
+ mMotionPauseDetector = new MotionPauseDetector(context);
+ mAllowLongClick = allowLongClick;
+ }
+
+ @Override
+ public int getType() {
+ return TYPE_ACCESSIBILITY | mDelegate.getType();
+ }
+
+ @Override
+ public void onMotionEvent(MotionEvent ev) {
+ if (mState != STATE_DELEGATE_ACTIVE) {
+ mVelocityTracker.addMovement(ev);
+ }
+
+ switch (ev.getActionMasked()) {
+ case ACTION_DOWN: {
+ break;
+ }
+ case ACTION_POINTER_UP: {
+ if (mState == STATE_ACTIVE) {
+ int pointerIndex = ev.getActionIndex();
+ int pointerId = ev.getPointerId(pointerIndex);
+ if (pointerId == mActivePointerId) {
+ final int newPointerIdx = pointerIndex == 0 ? 1 : 0;
+
+ mTotalY += (ev.getY(pointerIndex) - mDownY);
+ mDownY = ev.getY(newPointerIdx);
+ mActivePointerId = ev.getPointerId(newPointerIdx);
+ }
+ }
+ break;
+ }
+ case ACTION_POINTER_DOWN: {
+ if (mState == STATE_INACTIVE) {
+ if (mDelegate.allowInterceptByParent()) {
+ setActive(ev);
+
+ int pointerIndex = ev.getActionIndex();
+ mActivePointerId = ev.getPointerId(pointerIndex);
+ mDownY = ev.getY(pointerIndex);
+ } else {
+ mState = STATE_DELEGATE_ACTIVE;
+ }
+ }
+ break;
+ }
+ case ACTION_MOVE: {
+ if (mState == STATE_ACTIVE && mAllowLongClick) {
+ int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (pointerIndex == -1) {
+ break;
+ }
+
+ mMotionPauseDetector.addPosition(ev.getY(pointerIndex) - mDownY,
+ ev.getEventTime());
+ }
+ break;
+ }
+ case ACTION_UP:
+ if (mState == STATE_ACTIVE) {
+ try {
+ if (mAllowLongClick && mMotionPauseDetector.isPaused()) {
+ mSystemUiProxy.notifyAccessibilityButtonLongClicked();
+ } else {
+ mTotalY += (ev.getY() - mDownY);
+ mVelocityTracker.computeCurrentVelocity(1000);
+
+ if ((-mTotalY) > mMinGestureDistance
+ || (-mVelocityTracker.getYVelocity()) > mMinFlingVelocity) {
+ mSystemUiProxy.notifyAccessibilityButtonClicked(
+ Display.DEFAULT_DISPLAY);
+ }
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to notify accessibility event", e);
+ }
+ }
+ // Follow through
+ case ACTION_CANCEL: {
+ mVelocityTracker.recycle();
+ mMotionPauseDetector.clear();
+ break;
+ }
+ }
+
+ if (mState != STATE_ACTIVE) {
+ mDelegate.onMotionEvent(ev);
+ }
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java
index 5e7faf7..829e478 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java
@@ -50,20 +50,10 @@
/**
* Touch consumer for handling events to launch assistant from launcher
*/
-public class AssistantTouchConsumer implements InputConsumer {
+public class AssistantTouchConsumer extends DelegateInputConsumer {
private static final String TAG = "AssistantTouchConsumer";
private static final long RETRACT_ANIMATION_DURATION_MS = 300;
- /* The assistant touch consume competes with quick switch InputConsumer gesture. The delegate
- * can be chosen to run if the angle passing the slop is lower than the threshold angle. When
- * this occurs, the state changes to {@link #STATE_DELEGATE_ACTIVE} where the next incoming
- * motion events are handled by the delegate instead of the assistant touch consumer. If the
- * angle is higher than the threshold, the state will change to {@link #STATE_ASSISTANT_ACTIVE}.
- */
- private static final int STATE_INACTIVE = 0;
- private static final int STATE_ASSISTANT_ACTIVE = 1;
- private static final int STATE_DELEGATE_ACTIVE = 2;
-
private static final String INVOCATION_TYPE_KEY = "invocation_type";
private static final int INVOCATION_TYPE_GESTURE = 1;
@@ -78,7 +68,6 @@
private float mTimeFraction;
private long mDragTime;
private float mLastProgress;
- private int mState;
private int mDirection;
private ActivityControlHelper mActivityControlHelper;
@@ -87,46 +76,25 @@
private final int mAngleThreshold;
private final float mSlop;
private final ISystemUiProxy mSysUiProxy;
- private final InputConsumer mConsumerDelegate;
private final Context mContext;
- private final InputMonitorCompat mInputMonitorCompat;
-
-
public AssistantTouchConsumer(Context context, ISystemUiProxy systemUiProxy,
- InputConsumer delegate, InputMonitorCompat inputMonitorCompat,
- ActivityControlHelper activityControlHelper) {
+ ActivityControlHelper activityControlHelper, InputConsumer delegate,
+ InputMonitorCompat inputMonitor) {
+ super(delegate, inputMonitor);
final Resources res = context.getResources();
mContext = context;
mSysUiProxy = systemUiProxy;
- mConsumerDelegate = delegate;
mDistThreshold = res.getDimension(R.dimen.gestures_assistant_drag_threshold);
mTimeThreshold = res.getInteger(R.integer.assistant_gesture_min_time_threshold);
mAngleThreshold = res.getInteger(R.integer.assistant_gesture_corner_deg_threshold);
mSlop = QuickStepContract.getQuickStepDragSlopPx();
- mInputMonitorCompat = inputMonitorCompat;
mActivityControlHelper = activityControlHelper;
- mState = STATE_INACTIVE;
}
@Override
public int getType() {
- return TYPE_ASSISTANT;
- }
-
- @Override
- public boolean useSharedSwipeState() {
- if (mConsumerDelegate != null) {
- return mConsumerDelegate.useSharedSwipeState();
- }
- return false;
- }
-
- @Override
- public void onConsumerAboutToBeSwitched() {
- if (mConsumerDelegate != null) {
- mConsumerDelegate.onConsumerAboutToBeSwitched();
- }
+ return TYPE_ASSISTANT | mDelegate.getType();
}
@Override
@@ -158,6 +126,10 @@
if (mState == STATE_DELEGATE_ACTIVE) {
break;
}
+ if (!mDelegate.allowInterceptByParent()) {
+ mState = STATE_DELEGATE_ACTIVE;
+ break;
+ }
int pointerIndex = ev.findPointerIndex(mActivePointerId);
if (pointerIndex == -1) {
break;
@@ -168,9 +140,6 @@
// Normal gesture, ensure we pass the slop before we start tracking the gesture
if (Math.hypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y) > mSlop) {
- // Cancel touches to other windows (intercept)
- mInputMonitorCompat.pilferPointers();
-
mPassedSlop = true;
mStartDragPos.set(mLastPos.x, mLastPos.y);
mDragTime = SystemClock.uptimeMillis();
@@ -182,15 +151,7 @@
angle = angle > 90 ? 180 - angle : angle;
if (angle > mAngleThreshold && angle < 90) {
- mState = STATE_ASSISTANT_ACTIVE;
-
- if (mConsumerDelegate != null) {
- // Send cancel event
- MotionEvent event = MotionEvent.obtain(ev);
- event.setAction(MotionEvent.ACTION_CANCEL);
- mConsumerDelegate.onMotionEvent(event);
- event.recycle();
- }
+ setActive(ev);
} else {
mState = STATE_DELEGATE_ACTIVE;
}
@@ -232,8 +193,8 @@
break;
}
- if (mState != STATE_ASSISTANT_ACTIVE && mConsumerDelegate != null) {
- mConsumerDelegate.onMotionEvent(ev);
+ if (mState != STATE_ACTIVE) {
+ mDelegate.onMotionEvent(ev);
}
}
@@ -249,7 +210,8 @@
Bundle args = new Bundle();
args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_GESTURE);
- BaseDraggingActivity launcherActivity = mActivityControlHelper.getCreatedActivity();
+ BaseDraggingActivity launcherActivity =
+ mActivityControlHelper.getCreatedActivity();
if (launcherActivity != null) {
launcherActivity.getRootView().
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/DelegateInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/DelegateInputConsumer.java
new file mode 100644
index 0000000..d36162f
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/DelegateInputConsumer.java
@@ -0,0 +1,49 @@
+package com.android.quickstep;
+
+import android.view.MotionEvent;
+
+import com.android.systemui.shared.system.InputMonitorCompat;
+
+public abstract class DelegateInputConsumer implements InputConsumer {
+
+ protected static final int STATE_INACTIVE = 0;
+ protected static final int STATE_ACTIVE = 1;
+ protected static final int STATE_DELEGATE_ACTIVE = 2;
+
+ protected final InputConsumer mDelegate;
+ protected final InputMonitorCompat mInputMonitor;
+
+ protected int mState;
+
+ public DelegateInputConsumer(InputConsumer delegate, InputMonitorCompat inputMonitor) {
+ mDelegate = delegate;
+ mInputMonitor = inputMonitor;
+ mState = STATE_INACTIVE;
+ }
+
+ @Override
+ public boolean useSharedSwipeState() {
+ return mDelegate.useSharedSwipeState();
+ }
+
+ @Override
+ public boolean allowInterceptByParent() {
+ return mDelegate.allowInterceptByParent() && mState != STATE_ACTIVE;
+ }
+
+ @Override
+ public void onConsumerAboutToBeSwitched() {
+ mDelegate.onConsumerAboutToBeSwitched();
+ }
+
+ protected void setActive(MotionEvent ev) {
+ mState = STATE_ACTIVE;
+ mInputMonitor.pilferPointers();
+
+ // Send cancel event
+ MotionEvent event = MotionEvent.obtain(ev);
+ event.setAction(MotionEvent.ACTION_CANCEL);
+ mDelegate.onMotionEvent(event);
+ event.recycle();
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/InputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/InputConsumer.java
index e3f9e02..37b7288 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/InputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/InputConsumer.java
@@ -24,11 +24,12 @@
@TargetApi(Build.VERSION_CODES.O)
public interface InputConsumer {
- int TYPE_NO_OP = 0;
- int TYPE_OVERVIEW = 1;
- int TYPE_OTHER_ACTIVITY = 2;
- int TYPE_ASSISTANT = 3;
- int TYPE_DEVICE_LOCKED = 4;
+ int TYPE_NO_OP = 1 << 0;
+ int TYPE_OVERVIEW = 1 << 1;
+ int TYPE_OTHER_ACTIVITY = 1 << 2;
+ int TYPE_ASSISTANT = 1 << 3;
+ int TYPE_DEVICE_LOCKED = 1 << 4;
+ int TYPE_ACCESSIBILITY = 1 << 5;
InputConsumer NO_OP = () -> TYPE_NO_OP;
@@ -39,6 +40,13 @@
}
/**
+ * Returns true if the user has crossed the threshold for it to be an explicit action.
+ */
+ default boolean allowInterceptByParent() {
+ return true;
+ }
+
+ /**
* Called by the event queue when the consumer is about to be switched to a new consumer.
*/
default void onConsumerAboutToBeSwitched() { }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
index a033402..3d2659d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -308,7 +308,10 @@
SCALE_PROPERTY.set(recentsView, targetRvScale);
recentsView.setTranslationY(0);
ClipAnimationHelper clipHelper = new ClipAnimationHelper(launcher);
+ float tmpCurveScale = v.getCurveScale();
+ v.setCurveScale(1f);
clipHelper.fromTaskThumbnailView(v.getThumbnail(), (RecentsView) v.getParent(), null);
+ v.setCurveScale(tmpCurveScale);
SCALE_PROPERTY.set(recentsView, prevRvScale);
recentsView.setTranslationY(prevRvTransY);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
index 5dc641f..507535e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
@@ -21,6 +21,7 @@
import static android.view.MotionEvent.ACTION_POINTER_UP;
import static android.view.MotionEvent.ACTION_UP;
import static android.view.MotionEvent.INVALID_POINTER_ID;
+
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import static com.android.launcher3.util.RaceConditionTracker.ENTER;
import static com.android.launcher3.util.RaceConditionTracker.EXIT;
@@ -345,18 +346,21 @@
*/
private void finishTouchTracking(MotionEvent ev) {
if (mPassedDragSlop && mInteractionHandler != null) {
+ if (ev.getActionMasked() == ACTION_CANCEL) {
+ mInteractionHandler.onGestureCancelled();
+ } else {
+ mVelocityTracker.computeCurrentVelocity(1000,
+ ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
+ float velocityX = mVelocityTracker.getXVelocity(mActivePointerId);
+ float velocityY = mVelocityTracker.getYVelocity(mActivePointerId);
+ float velocity = isNavBarOnRight() ? velocityX
+ : isNavBarOnLeft() ? -velocityX
+ : velocityY;
- mVelocityTracker.computeCurrentVelocity(1000,
- ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
- float velocityX = mVelocityTracker.getXVelocity(mActivePointerId);
- float velocityY = mVelocityTracker.getYVelocity(mActivePointerId);
- float velocity = isNavBarOnRight() ? velocityX
- : isNavBarOnLeft() ? -velocityX
- : velocityY;
-
- mInteractionHandler.updateDisplacement(getDisplacement(ev) - mStartDisplacement);
- mInteractionHandler.onGestureEnded(velocity, new PointF(velocityX, velocityY),
- mDownPos);
+ mInteractionHandler.updateDisplacement(getDisplacement(ev) - mStartDisplacement);
+ mInteractionHandler.onGestureEnded(velocity, new PointF(velocityX, velocityY),
+ mDownPos);
+ }
} else {
// Since we start touch tracking on DOWN, we may reach this state without actually
// starting the gesture. In that case, just cleanup immediately.
@@ -387,7 +391,7 @@
mSwipeSharedState.canGestureBeContinued = endTarget != null && endTarget.canBeContinued;
mSwipeSharedState.goingToLauncher = endTarget != null && endTarget.isLauncher;
if (mSwipeSharedState.canGestureBeContinued) {
- mInteractionHandler.cancel();
+ mInteractionHandler.cancelCurrentAnimation();
} else {
mInteractionHandler.reset();
}
@@ -425,4 +429,9 @@
public boolean useSharedSwipeState() {
return mInteractionHandler != null;
}
+
+ @Override
+ public boolean allowInterceptByParent() {
+ return !mPassedTouchSlop;
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java
index b803071..b48e3de 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java
@@ -15,24 +15,20 @@
*/
package com.android.quickstep;
-import static android.view.MotionEvent.ACTION_CANCEL;
-import static android.view.MotionEvent.ACTION_DOWN;
-import static android.view.MotionEvent.ACTION_MOVE;
-import static android.view.MotionEvent.ACTION_UP;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
-import android.graphics.PointF;
import android.view.KeyEvent;
import android.view.MotionEvent;
-import android.view.ViewConfiguration;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.Utilities;
import com.android.launcher3.views.BaseDragLayer;
-import com.android.quickstep.util.CachedEventDispatcher;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.InputMonitorCompat;
+
+import androidx.annotation.Nullable;
/**
* Input consumer for handling touch on the recents/Launcher activity.
@@ -40,24 +36,27 @@
public class OverviewInputConsumer<T extends BaseDraggingActivity>
implements InputConsumer {
- private final CachedEventDispatcher mCachedEventDispatcher = new CachedEventDispatcher();
private final T mActivity;
private final BaseDragLayer mTarget;
+ private final InputMonitorCompat mInputMonitor;
+
private final int[] mLocationOnScreen = new int[2];
- private final PointF mDownPos = new PointF();
- private final int mTouchSlopSquared;
+ private final boolean mProxyTouch;
private final boolean mStartingInActivityBounds;
+ private boolean mTargetHandledTouch;
- private boolean mTrackingStarted = false;
- private boolean mInvalidated = false;
-
- OverviewInputConsumer(T activity, boolean startingInActivityBounds) {
+ OverviewInputConsumer(T activity, @Nullable InputMonitorCompat inputMonitor,
+ boolean startingInActivityBounds) {
mActivity = activity;
- mTarget = activity.getDragLayer();
- int touchSlop = ViewConfiguration.get(mActivity).getScaledTouchSlop();
- mTouchSlopSquared = touchSlop * touchSlop;
+ mInputMonitor = inputMonitor;
mStartingInActivityBounds = startingInActivityBounds;
+
+ mTarget = activity.getDragLayer();
+ if (!startingInActivityBounds) {
+ mTarget.getLocationOnScreen(mLocationOnScreen);
+ }
+ mProxyTouch = mTarget.prepareProxyEventStarting();
}
@Override
@@ -66,46 +65,35 @@
}
@Override
+ public boolean allowInterceptByParent() {
+ return !mTargetHandledTouch;
+ }
+
+ @Override
public void onMotionEvent(MotionEvent ev) {
- if (mInvalidated) {
+ if (!mProxyTouch) {
return;
}
- mCachedEventDispatcher.dispatchEvent(ev);
- int action = ev.getActionMasked();
- if (action == ACTION_DOWN) {
- if (mStartingInActivityBounds) {
- startTouchTracking(ev, false /* updateLocationOffset */,
- false /* closeActiveWindows */);
- return;
- }
- mTrackingStarted = false;
- mDownPos.set(ev.getX(), ev.getY());
- } else if (!mTrackingStarted) {
- switch (action) {
- case ACTION_CANCEL:
- case ACTION_UP:
- startTouchTracking(ev, true /* updateLocationOffset */,
- false /* closeActiveWindows */);
- break;
- case ACTION_MOVE: {
- float x = ev.getX() - mDownPos.x;
- float y = ev.getY() - mDownPos.y;
- double hypotSquared = x * x + y * y;
- if (hypotSquared >= mTouchSlopSquared) {
- // Start tracking only when touch slop is crossed.
- startTouchTracking(ev, true /* updateLocationOffset */,
- true /* closeActiveWindows */);
- }
- }
- }
+
+ int flags = ev.getEdgeFlags();
+ if (!mStartingInActivityBounds) {
+ ev.setEdgeFlags(flags | Utilities.EDGE_NAV_BAR);
}
+ ev.offsetLocation(-mLocationOnScreen[0], -mLocationOnScreen[1]);
+ boolean handled = mTarget.proxyTouchEvent(ev);
+ ev.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]);
+ ev.setEdgeFlags(flags);
- if (action == ACTION_UP || action == ACTION_CANCEL) {
- mInvalidated = true;
-
- // Set an empty consumer to that all the cached events are cleared
- if (!mCachedEventDispatcher.hasConsumer()) {
- mCachedEventDispatcher.setConsumer(motionEvent -> { });
+ if (!mTargetHandledTouch && handled) {
+ mTargetHandledTouch = true;
+ if (!mStartingInActivityBounds) {
+ OverviewCallbacks.get(mActivity).closeAllWindows();
+ ActivityManagerWrapper.getInstance()
+ .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+ TOUCH_INTERACTION_LOG.addLog("startQuickstep");
+ }
+ if (mInputMonitor != null) {
+ mInputMonitor.pilferPointers();
}
}
}
@@ -117,42 +105,12 @@
}
}
- private void startTouchTracking(MotionEvent ev, boolean updateLocationOffset,
- boolean closeActiveWindows) {
- if (updateLocationOffset) {
- mTarget.getLocationOnScreen(mLocationOnScreen);
- }
-
- if (closeActiveWindows) {
- OverviewCallbacks.get(mActivity).closeAllWindows();
- ActivityManagerWrapper.getInstance()
- .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
- TOUCH_INTERACTION_LOG.addLog("startQuickstep");
- }
-
- mTrackingStarted = true;
- mCachedEventDispatcher.setConsumer(this::sendEvent);
-
- }
-
- private void sendEvent(MotionEvent ev) {
- if (mInvalidated) {
- return;
- }
- int flags = ev.getEdgeFlags();
- ev.setEdgeFlags(flags | Utilities.EDGE_NAV_BAR);
- ev.offsetLocation(-mLocationOnScreen[0], -mLocationOnScreen[1]);
- mInvalidated = !mTarget.dispatchTouchEvent(this, ev);
- ev.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]);
- ev.setEdgeFlags(flags);
- }
-
public static InputConsumer newInstance(ActivityControlHelper activityHelper,
- boolean startingInActivityBounds) {
+ @Nullable InputMonitorCompat inputMonitor, boolean startingInActivityBounds) {
BaseDraggingActivity activity = activityHelper.getCreatedActivity();
if (activity == null) {
return InputConsumer.NO_OP;
}
- return new OverviewInputConsumer(activity, startingInActivityBounds);
+ return new OverviewInputConsumer(activity, inputMonitor, startingInActivityBounds);
}
}
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java
index 7c6638a..f393387 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java
@@ -51,9 +51,16 @@
mLastAnimationRunning = true;
}
+ private void clearAnimationTarget() {
+ if (mLastAnimationTarget != null) {
+ mLastAnimationTarget.release();
+ mLastAnimationTarget = null;
+ }
+ }
+
@Override
public final void onRecentsAnimationCanceled() {
- mLastAnimationTarget = null;
+ clearAnimationTarget();
mLastAnimationCancelled = true;
mLastAnimationRunning = false;
@@ -64,7 +71,7 @@
mRecentsAnimationListener.removeListener(this);
}
mRecentsAnimationListener = null;
- mLastAnimationTarget = null;
+ clearAnimationTarget();
mLastAnimationCancelled = false;
mLastAnimationRunning = false;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
index 4526d67..5b94002 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
@@ -19,6 +19,8 @@
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.ComponentName;
import android.graphics.RectF;
@@ -109,8 +111,14 @@
*/
public static ValueAnimator getRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
RemoteAnimationTargetCompat[] targets, final ClipAnimationHelper inOutHelper) {
+ SyncRtSurfaceTransactionApplierCompat applier =
+ new SyncRtSurfaceTransactionApplierCompat(v);
ClipAnimationHelper.TransformParams params = new ClipAnimationHelper.TransformParams()
- .setSyncTransactionApplier(new SyncRtSurfaceTransactionApplierCompat(v));
+ .setSyncTransactionApplier(applier);
+
+ final RemoteAnimationTargetSet targetSet =
+ new RemoteAnimationTargetSet(targets, MODE_OPENING);
+ targetSet.addDependentTransactionApplier(applier);
final ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
appAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
@@ -120,17 +128,15 @@
final FloatProp mViewAlpha = new FloatProp(1f, 0f, 75, 75, LINEAR);
final FloatProp mTaskAlpha = new FloatProp(0f, 1f, 0, 75, LINEAR);
- final RemoteAnimationTargetSet mTargetSet;
final RectF mThumbnailRect;
{
- mTargetSet = new RemoteAnimationTargetSet(targets, MODE_OPENING);
inOutHelper.setTaskAlphaCallback((t, alpha) -> mTaskAlpha.value);
inOutHelper.prepareAnimation(true /* isOpening */);
inOutHelper.fromTaskThumbnailView(v.getThumbnail(), (RecentsView) v.getParent(),
- mTargetSet.apps.length == 0 ? null : mTargetSet.apps[0]);
+ targetSet.apps.length == 0 ? null : targetSet.apps[0]);
mThumbnailRect = new RectF(inOutHelper.getTargetRect());
mThumbnailRect.offset(-v.getTranslationX(), -v.getTranslationY());
@@ -140,7 +146,7 @@
@Override
public void onUpdate(float percent) {
params.setProgress(1 - percent);
- RectF taskBounds = inOutHelper.applyTransform(mTargetSet, params);
+ RectF taskBounds = inOutHelper.applyTransform(targetSet, params);
if (!skipViewChanges) {
float scale = taskBounds.width() / mThumbnailRect.width();
v.setScaleX(scale);
@@ -151,6 +157,12 @@
}
}
});
+ appAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ targetSet.release();
+ }
+ });
return appAnimator;
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index fc3f332..c91bb1b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -20,6 +20,8 @@
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
@@ -67,9 +69,8 @@
import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.InputMonitorCompat;
-
-import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -456,25 +457,32 @@
final ActivityControlHelper activityControl =
mOverviewComponentObserver.getActivityControlHelper();
+ InputConsumer base;
if (runningTaskInfo == null && !mSwipeSharedState.goingToLauncher) {
- return InputConsumer.NO_OP;
- } else if (mAssistantAvailable
- && SysUINavigationMode.INSTANCE.get(this).getMode() == Mode.NO_BUTTON
- && AssistantTouchConsumer.withinTouchRegion(this, event)) {
-
- boolean addDelegate = !activityControl.isResumed();
- return new AssistantTouchConsumer(this, mISystemUiProxy, addDelegate ?
- createOtherActivityInputConsumer(event, runningTaskInfo) : null,
- mInputMonitorCompat, activityControl);
-
+ base = InputConsumer.NO_OP;
} else if (mSwipeSharedState.goingToLauncher || activityControl.isResumed()) {
- return OverviewInputConsumer.newInstance(activityControl, false);
+ base = OverviewInputConsumer.newInstance(activityControl, mInputMonitorCompat, false);
} else if (ENABLE_QUICKSTEP_LIVE_TILE.get() &&
activityControl.isInLiveTileMode()) {
- return OverviewInputConsumer.newInstance(activityControl, false);
+ base = OverviewInputConsumer.newInstance(activityControl, mInputMonitorCompat, false);
} else {
- return createOtherActivityInputConsumer(event, runningTaskInfo);
+ base = createOtherActivityInputConsumer(event, runningTaskInfo);
}
+
+ if (mMode == Mode.NO_BUTTON) {
+ if (mAssistantAvailable && AssistantTouchConsumer.withinTouchRegion(this, event)) {
+ base = new AssistantTouchConsumer(this, mISystemUiProxy, activityControl, base,
+ mInputMonitorCompat);
+ }
+
+ if ((mSystemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0) {
+ base = new AccessibilityInputConsumer(this, mISystemUiProxy,
+ (mSystemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0, base,
+ mInputMonitorCompat);
+ }
+ }
+
+ return base;
}
private OtherActivityInputConsumer createOtherActivityInputConsumer(MotionEvent event,
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
index afc4fcb..3797e87 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -250,7 +250,6 @@
private T mActivity;
private RecentsView mRecentsView;
- private SyncRtSurfaceTransactionApplierCompat mSyncTransactionApplier;
private AnimationFactory mAnimationFactory = (t) -> { };
private LiveTileOverlay mLiveTileOverlay = new LiveTileOverlay();
@@ -405,8 +404,11 @@
}
mRecentsView = activity.getOverviewPanel();
- SyncRtSurfaceTransactionApplierCompat.create(mRecentsView,
- applier -> mSyncTransactionApplier = applier );
+ SyncRtSurfaceTransactionApplierCompat.create(mRecentsView, applier -> {
+ mTransformParams.setSyncTransactionApplier(applier);
+ mRecentsAnimationWrapper.runOnInit(() ->
+ mRecentsAnimationWrapper.targetSet.addDependentTransactionApplier(applier));
+ });
mRecentsView.setEnableFreeScroll(false);
mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
@@ -648,8 +650,7 @@
}
float offsetScale = getTaskCurveScaleForOffsetX(offsetX,
mClipAnimationHelper.getTargetRect().width());
- mTransformParams.setProgress(shift).setOffsetX(offsetX).setOffsetScale(offsetScale)
- .setSyncTransactionApplier(mSyncTransactionApplier);
+ mTransformParams.setProgress(shift).setOffsetX(offsetX).setOffsetScale(offsetScale);
mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targetSet,
mTransformParams);
mRecentsAnimationWrapper.setWindowThresholdCrossed(
@@ -769,6 +770,17 @@
}
/**
+ * Called as a result on ACTION_CANCEL to return the UI to the start state.
+ */
+ @UiThread
+ public void onGestureCancelled() {
+ updateDisplacement(0);
+ setStateOnUiThread(STATE_GESTURE_COMPLETED);
+ mLogAction = Touch.SWIPE_NOOP;
+ handleNormalGestureEnd(0, false, new PointF(), true /* isCancel */);
+ }
+
+ /**
* @param endVelocity The velocity in the direction of the nav bar to the middle of the screen.
* @param velocity The x and y components of the velocity when the gesture ends.
* @param downPos The x and y value of where the gesture started.
@@ -788,7 +800,7 @@
mLogDirection = velocity.x < 0 ? Direction.LEFT : Direction.RIGHT;
}
mDownPos = downPos;
- handleNormalGestureEnd(endVelocity, isFling, velocity);
+ handleNormalGestureEnd(endVelocity, isFling, velocity, false /* isCancel */);
}
@UiThread
@@ -802,11 +814,12 @@
setTargetAlphaProvider(WindowTransformSwipeHandler::getHiddenTargetAlpha);
}
- return OverviewInputConsumer.newInstance(mActivityControlHelper, true);
+ return OverviewInputConsumer.newInstance(mActivityControlHelper, null, true);
}
@UiThread
- private void handleNormalGestureEnd(float endVelocity, boolean isFling, PointF velocity) {
+ private void handleNormalGestureEnd(float endVelocity, boolean isFling, PointF velocity,
+ boolean isCancel) {
PointF velocityPxPerMs = new PointF(velocity.x / 1000, velocity.y / 1000);
long duration = MAX_SWIPE_DURATION;
float currentShift = mCurrentShift.value;
@@ -824,7 +837,9 @@
}
final boolean reachedOverviewThreshold = currentShift >= MIN_PROGRESS_FOR_OVERVIEW;
if (!isFling) {
- if (mMode == Mode.NO_BUTTON) {
+ if (isCancel) {
+ endTarget = LAST_TASK;
+ } else if (mMode == Mode.NO_BUTTON) {
if (mIsShelfPeeking) {
endTarget = RECENTS;
} else if (goingToNewTask) {
@@ -907,7 +922,7 @@
private void doLogGesture(GestureEndTarget endTarget) {
DeviceProfile dp = mDp;
- if (dp == null) {
+ if (dp == null || mDownPos == null) {
// We probably never received an animation controller, skip logging.
return;
}
@@ -1047,8 +1062,7 @@
float iconAlpha = Utilities.mapToRange(interpolatedProgress, 0,
windowAlphaThreshold, 0f, 1f, Interpolators.LINEAR);
- mTransformParams.setCurrentRectAndTargetAlpha(currentRect, 1f - iconAlpha)
- .setSyncTransactionApplier(mSyncTransactionApplier);
+ mTransformParams.setCurrentRectAndTargetAlpha(currentRect, 1f - iconAlpha);
mClipAnimationHelper.applyTransform(targetSet, mTransformParams,
false /* launcherOnTop */);
@@ -1106,7 +1120,11 @@
setStateOnUiThread(STATE_HANDLER_INVALIDATED);
}
- public void cancel() {
+ /**
+ * Cancels any running animation so that the active target can be overriden by a new swipe
+ * handle (in case of quick switch).
+ */
+ public void cancelCurrentAnimation() {
mCurrentShift.cancelAnimation();
if (mLauncherTransitionController != null && mLauncherTransitionController
.getAnimationPlayer().isStarted()) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsRootView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsRootView.java
index 777e592..09d323e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsRootView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsRootView.java
@@ -17,18 +17,13 @@
import android.annotation.TargetApi;
import android.content.Context;
-import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
-import android.graphics.RectF;
import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.ViewDebug;
import android.view.WindowInsets;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.BaseDragLayer;
@@ -39,9 +34,6 @@
private static final int MIN_SIZE = 10;
private final RecentsActivity mActivity;
- @ViewDebug.ExportedProperty(category = "launcher")
- private final RectF mTouchExcludeRegion = new RectF();
-
private final Point mLastKnownSize = new Point(MIN_SIZE, MIN_SIZE);
public RecentsRootView(Context context, AttributeSet attrs) {
@@ -100,26 +92,7 @@
@Override
public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
- if (Utilities.ATLEAST_Q) {
- Insets gestureInsets = insets.getMandatorySystemGestureInsets();
- mTouchExcludeRegion.set(gestureInsets.left, gestureInsets.top,
- gestureInsets.right, gestureInsets.bottom);
- }
+ updateTouchExcludeRegion(insets);
return super.dispatchApplyWindowInsets(insets);
}
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- float x = ev.getX();
- float y = ev.getY();
- if (y < mTouchExcludeRegion.top
- || x < mTouchExcludeRegion.left
- || x > (getWidth() - mTouchExcludeRegion.right)
- || y > (getHeight() - mTouchExcludeRegion.bottom)) {
- return false;
- }
- }
- return super.dispatchTouchEvent(ev);
- }
}
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java
index 5a1a103..83973fa 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java
@@ -95,9 +95,4 @@
void onRecentsAnimationCanceled();
}
-
- public interface SwipeAnimationFinishListener {
-
- void onSwipeAnimationFinished(SwipeAnimationTargetSet targetSet);
- }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index 848c214..298c562 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -492,7 +492,7 @@
return 1 - curveInterpolation * EDGE_SCALE_DOWN_FACTOR;
}
- private void setCurveScale(float curveScale) {
+ public void setCurveScale(float curveScale) {
mCurveScale = curveScale;
onScaleChanged();
}
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index cda9d4f..886dcc3 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -416,10 +416,9 @@
RemoteAnimationTargetSet openingTargets = new RemoteAnimationTargetSet(targets,
MODE_OPENING);
- RemoteAnimationTargetSet closingTargets = new RemoteAnimationTargetSet(targets,
- MODE_CLOSING);
SyncRtSurfaceTransactionApplierCompat surfaceApplier =
new SyncRtSurfaceTransactionApplierCompat(mFloatingView);
+ openingTargets.addDependentTransactionApplier(surfaceApplier);
// Scale the app icon to take up the entire screen. This simplifies the math when
// animating the app window position / scale.
@@ -470,6 +469,7 @@
if (v instanceof BubbleTextView) {
((BubbleTextView) v).setStayPressed(false);
}
+ openingTargets.release();
}
});
diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationTargetSet.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationTargetSet.java
index c372485..0df4e94 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteAnimationTargetSet.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationTargetSet.java
@@ -16,14 +16,20 @@
package com.android.quickstep.util;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
+import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Queue;
/**
* Holds a collection of RemoteAnimationTargets, filtered by different properties.
*/
public class RemoteAnimationTargetSet {
+ private final Queue<SyncRtSurfaceTransactionApplierCompat> mDependentTransactionAppliers =
+ new ArrayDeque<>(1);
+
public final RemoteAnimationTargetCompat[] unfilteredApps;
public final RemoteAnimationTargetCompat[] apps;
public final int targetMode;
@@ -60,4 +66,19 @@
}
return false;
}
+
+ public void addDependentTransactionApplier(SyncRtSurfaceTransactionApplierCompat delay) {
+ mDependentTransactionAppliers.add(delay);
+ }
+
+ public void release() {
+ SyncRtSurfaceTransactionApplierCompat applier = mDependentTransactionAppliers.poll();
+ if (applier == null) {
+ for (RemoteAnimationTargetCompat target : unfilteredApps) {
+ target.release();
+ }
+ } else {
+ applier.addAfterApplyCallback(this::release);
+ }
+ }
}
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
index e552f56..93e403c 100644
--- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -71,7 +71,8 @@
@Override
public Statement apply(Statement base, Description description) {
- if (TestHelpers.isInLauncherProcess() &&
+ // b/130558787; b/131419978
+ if (false && TestHelpers.isInLauncherProcess() &&
description.getAnnotation(NavigationModeSwitch.class) != null) {
Mode mode = description.getAnnotation(NavigationModeSwitch.class).mode();
return new Statement() {
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index e6c2d0c..90e673b 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -8,13 +8,10 @@
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.Insets;
import android.graphics.Paint;
import android.graphics.Rect;
-import android.graphics.RectF;
import android.os.Build;
import android.util.AttributeSet;
-import android.view.MotionEvent;
import android.view.View;
import android.view.ViewDebug;
import android.view.WindowInsets;
@@ -32,9 +29,6 @@
private final Rect mConsumedInsets = new Rect();
@ViewDebug.ExportedProperty(category = "launcher")
- private final RectF mTouchExcludeRegion = new RectF();
-
- @ViewDebug.ExportedProperty(category = "launcher")
private static final List<Rect> SYSTEM_GESTURE_EXCLUSION_RECT =
Collections.singletonList(new Rect());
@@ -164,30 +158,11 @@
@Override
public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
- if (Utilities.ATLEAST_Q) {
- Insets gestureInsets = insets.getMandatorySystemGestureInsets();
- mTouchExcludeRegion.set(gestureInsets.left, gestureInsets.top,
- gestureInsets.right, gestureInsets.bottom);
- }
+ mLauncher.getDragLayer().updateTouchExcludeRegion(insets);
return super.dispatchApplyWindowInsets(insets);
}
@Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- float x = ev.getX();
- float y = ev.getY();
- if (y < mTouchExcludeRegion.top
- || x < mTouchExcludeRegion.left
- || x > (getWidth() - mTouchExcludeRegion.right)
- || y > (getHeight() - mTouchExcludeRegion.bottom)) {
- return false;
- }
- }
- return super.dispatchTouchEvent(ev);
- }
-
- @Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
SYSTEM_GESTURE_EXCLUSION_RECT.get(0).set(l, t, r, b);
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 593dbd4..c7d93fe 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -22,7 +22,6 @@
import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import static com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
import static com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import android.animation.AnimatorSet;
import android.animation.LayoutTransition;
@@ -173,8 +172,7 @@
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
BaseDragLayer dl = getPopupContainer();
- final boolean cameFromNavBar = (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0;
- if (!cameFromNavBar && !dl.isEventOverView(this, ev)) {
+ if (!dl.isEventOverView(this, ev)) {
mLauncher.getUserEventDispatcher().logActionTapOutside(
LoggerUtils.newContainerTarget(ContainerType.DEEPSHORTCUTS));
close(true);
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index 66cd536..8a15220 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -22,13 +22,19 @@
import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
+import android.annotation.TargetApi;
import android.content.Context;
+import android.graphics.Insets;
import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.Build;
import android.util.AttributeSet;
import android.util.Property;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewDebug;
import android.view.ViewGroup;
+import android.view.WindowInsets;
import android.view.accessibility.AccessibilityEvent;
import android.widget.FrameLayout;
@@ -74,18 +80,32 @@
}
};
+ // Touch is being dispatched through the normal view dispatch system
+ private static final int TOUCH_DISPATCHING_VIEW = 1 << 0;
+ // Touch is being dispatched through the normal view dispatch system, and started at the
+ // system gesture region
+ private static final int TOUCH_DISPATCHING_GESTURE = 1 << 1;
+ // Touch is being dispatched through a proxy from InputMonitor
+ private static final int TOUCH_DISPATCHING_PROXY = 1 << 2;
+
protected final int[] mTmpXY = new int[2];
protected final Rect mHitRect = new Rect();
+ @ViewDebug.ExportedProperty(category = "launcher")
+ private final RectF mSystemGestureRegion = new RectF();
+ private int mTouchDispatchState = 0;
+
protected final T mActivity;
private final MultiValueAlpha mMultiValueAlpha;
+ // All the touch controllers for the view
protected TouchController[] mControllers;
+ // Touch controller which is currently active for the normal view dispatch
protected TouchController mActiveController;
- private TouchCompleteListener mTouchCompleteListener;
+ // Touch controller which is being used for the proxy events
+ protected TouchController mProxyTouchController;
- // Object controlling the current touch interaction
- private Object mCurrentTouchOwner;
+ private TouchCompleteListener mTouchCompleteListener;
public BaseDragLayer(Context context, AttributeSet attrs, int alphaChannelCount) {
super(context, attrs);
@@ -113,30 +133,36 @@
return findActiveController(ev);
}
+ private TouchController findControllerToHandleTouch(MotionEvent ev) {
+ AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
+ if (topView != null && topView.onControllerInterceptTouchEvent(ev)) {
+ return topView;
+ }
+
+ for (TouchController controller : mControllers) {
+ if (controller.onControllerInterceptTouchEvent(ev)) {
+ return controller;
+ }
+ }
+ return null;
+ }
+
protected boolean findActiveController(MotionEvent ev) {
if (com.android.launcher3.TestProtocol.sDebugTracing) {
android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
"mActiveController = null");
}
mActiveController = null;
+ if ((mTouchDispatchState & (TOUCH_DISPATCHING_GESTURE | TOUCH_DISPATCHING_PROXY)) == 0) {
+ // Only look for controllers if we are not dispatching from gesture area and proxy is
+ // not active
+ mActiveController = findControllerToHandleTouch(ev);
- AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
- if (topView != null && topView.onControllerInterceptTouchEvent(ev)) {
- if (com.android.launcher3.TestProtocol.sDebugTracing) {
- android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
- "setting controller1: " + topView.getClass().getSimpleName());
- }
- mActiveController = topView;
- return true;
- }
-
- for (TouchController controller : mControllers) {
- if (controller.onControllerInterceptTouchEvent(ev)) {
+ if (mActiveController != null) {
if (com.android.launcher3.TestProtocol.sDebugTracing) {
android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
- "setting controller1: " + controller.getClass().getSimpleName());
+ "setting controller1: " + mActiveController.getClass().getSimpleName());
}
- mActiveController = controller;
return true;
}
}
@@ -223,40 +249,75 @@
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
- return dispatchTouchEvent(this, ev);
- }
+ switch (ev.getAction()) {
+ case ACTION_DOWN: {
+ float x = ev.getX();
+ float y = ev.getY();
+ mTouchDispatchState |= TOUCH_DISPATCHING_VIEW;
- public boolean dispatchTouchEvent(Object caller, MotionEvent ev) {
- return verifyTouchDispatch(caller, ev) && super.dispatchTouchEvent(ev);
+ if ((y < mSystemGestureRegion.top
+ || x < mSystemGestureRegion.left
+ || x > (getWidth() - mSystemGestureRegion.right)
+ || y > (getHeight() - mSystemGestureRegion.bottom))) {
+ mTouchDispatchState |= TOUCH_DISPATCHING_GESTURE;
+ } else {
+ mTouchDispatchState &= ~TOUCH_DISPATCHING_GESTURE;
+ }
+ break;
+ }
+ case ACTION_CANCEL:
+ case ACTION_UP:
+ mTouchDispatchState &= ~TOUCH_DISPATCHING_GESTURE;
+ mTouchDispatchState &= ~TOUCH_DISPATCHING_VIEW;
+ break;
+ }
+ super.dispatchTouchEvent(ev);
+
+ // We want to get all events so that mTouchDispatchSource is maintained properly
+ return true;
}
/**
- * Returns true if the {@param caller} is allowed to dispatch {@param ev} on this view,
- * false otherwise.
+ * Called before we are about to receive proxy events.
+ *
+ * @return false if we can't handle proxy at this time
*/
- private boolean verifyTouchDispatch(Object caller, MotionEvent ev) {
- int action = ev.getAction();
- if (action == ACTION_DOWN) {
- if (mCurrentTouchOwner != null) {
- // Another touch in progress.
- ev.setAction(ACTION_CANCEL);
- super.dispatchTouchEvent(ev);
- ev.setAction(action);
- }
- mCurrentTouchOwner = caller;
- return true;
- }
- if (mCurrentTouchOwner != caller) {
- // Someone else is controlling the touch
+ public boolean prepareProxyEventStarting() {
+ mProxyTouchController = null;
+ if ((mTouchDispatchState & TOUCH_DISPATCHING_VIEW) != 0 && mActiveController != null) {
+ // We are already dispatching using view system and have an active controller, we can't
+ // handle another controller.
+
+ // This flag was already cleared in proxy ACTION_UP or ACTION_CANCEL. Added here just
+ // to be safe
+ mTouchDispatchState &= ~TOUCH_DISPATCHING_PROXY;
return false;
}
- if (action == ACTION_UP || action == ACTION_CANCEL) {
- mCurrentTouchOwner = null;
- }
+
+ mTouchDispatchState |= TOUCH_DISPATCHING_PROXY;
return true;
}
/**
+ * Proxies the touch events to the gesture handlers
+ */
+ public boolean proxyTouchEvent(MotionEvent ev) {
+ boolean handled;
+ if (mProxyTouchController != null) {
+ handled = mProxyTouchController.onControllerTouchEvent(ev);
+ } else {
+ mProxyTouchController = findControllerToHandleTouch(ev);
+ handled = mProxyTouchController != null;
+ }
+ int action = ev.getAction();
+ if (action == ACTION_UP || action == ACTION_CANCEL) {
+ mProxyTouchController = null;
+ mTouchDispatchState &= ~TOUCH_DISPATCHING_PROXY;
+ }
+ return handled;
+ }
+
+ /**
* Determine the rect of the descendant in this DragLayer's coordinates
*
* @param descendant The descendant whose coordinates we want to find.
@@ -423,4 +484,13 @@
}
}
}
+
+ @TargetApi(Build.VERSION_CODES.Q)
+ public void updateTouchExcludeRegion(WindowInsets insets) {
+ if (Utilities.ATLEAST_Q) {
+ Insets gestureInsets = insets.getMandatorySystemGestureInsets();
+ mSystemGestureRegion.set(gestureInsets.left, gestureInsets.top,
+ gestureInsets.right, gestureInsets.bottom);
+ }
+ }
}
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index a37218b..3e84440 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -17,6 +17,10 @@
import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_2BUTTON_OVERLAY;
+import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_3BUTTON_OVERLAY;
+import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_GESTURAL_OVERLAY;
+
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -29,6 +33,7 @@
import android.content.IntentFilter;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.PackageManager;
+import android.os.Build;
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
@@ -57,6 +62,7 @@
import com.android.launcher3.util.rule.ShellCommandRule;
import org.junit.After;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.rules.TestRule;
@@ -67,6 +73,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import java.lang.reflect.Method;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -102,6 +109,68 @@
}
if (TestHelpers.isInLauncherProcess()) Utilities.enableRunningInTestHarnessForTests();
mLauncher = new LauncherInstrumentation(instrumentation);
+
+ // b/130558787; b/131419978
+ if (TestHelpers.isInLauncherProcess()) {
+ try {
+ Class systemProps = Class.forName("android.os.SystemProperties");
+ Method getInt = systemProps.getMethod("getInt", String.class, int.class);
+ int apiLevel = (int) getInt.invoke(null, "ro.product.first_api_level", 0);
+
+ if (apiLevel >= Build.VERSION_CODES.P) {
+ setActiveOverlay(NAV_BAR_MODE_2BUTTON_OVERLAY,
+ LauncherInstrumentation.NavigationModel.TWO_BUTTON);
+ }
+ if (apiLevel >= Build.VERSION_CODES.O && apiLevel < Build.VERSION_CODES.P) {
+ setActiveOverlay(NAV_BAR_MODE_GESTURAL_OVERLAY,
+ LauncherInstrumentation.NavigationModel.ZERO_BUTTON);
+ }
+ if (apiLevel < Build.VERSION_CODES.O) {
+ setActiveOverlay(NAV_BAR_MODE_3BUTTON_OVERLAY,
+ LauncherInstrumentation.NavigationModel.THREE_BUTTON);
+ }
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public void setActiveOverlay(String overlayPackage,
+ LauncherInstrumentation.NavigationModel expectedMode) {
+ setOverlayPackageEnabled(NAV_BAR_MODE_3BUTTON_OVERLAY,
+ overlayPackage == NAV_BAR_MODE_3BUTTON_OVERLAY);
+ setOverlayPackageEnabled(NAV_BAR_MODE_2BUTTON_OVERLAY,
+ overlayPackage == NAV_BAR_MODE_2BUTTON_OVERLAY);
+ setOverlayPackageEnabled(NAV_BAR_MODE_GESTURAL_OVERLAY,
+ overlayPackage == NAV_BAR_MODE_GESTURAL_OVERLAY);
+
+ for (int i = 0; i != 100; ++i) {
+ if (mLauncher.getNavigationModel() == expectedMode) {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ return;
+ }
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ Assert.fail("Couldn't switch to " + overlayPackage);
+ }
+
+ private void setOverlayPackageEnabled(String overlayPackage, boolean enable) {
+ Log.d(TAG, "setOverlayPackageEnabled: " + overlayPackage + " " + enable);
+ final String action = enable ? "enable" : "disable";
+ try {
+ UiDevice.getInstance(getInstrumentation()).executeShellCommand(
+ "cmd overlay " + action + " " + overlayPackage);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
}
@Rule
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index c55bc72..2a69757 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -239,12 +239,6 @@
// Test starting a workspace app.
final AppIcon app = workspace.tryGetWorkspaceAppIcon("Chrome");
assertNotNull("No Chrome app in workspace", app);
- assertNotNull("AppIcon.launch returned null",
- app.launch(resolveSystemApp(Intent.CATEGORY_APP_BROWSER)));
- executeOnLauncher(launcher -> assertTrue(
- "Launcher activity is the top activity; expecting another activity to be the top "
- + "one",
- isInBackground(launcher)));
}
public static void runIconLaunchFromAllAppsTest(AbstractLauncherUiTest test, AllApps allApps) {
@@ -340,6 +334,10 @@
dragToWorkspace().
getWorkspaceAppIcon(APP_NAME).
launch(getAppPackageName());
+ executeOnLauncher(launcher -> assertTrue(
+ "Launcher activity is the top activity; expecting another activity to be the "
+ + "top one",
+ isInBackground(launcher)));
} finally {
TestProtocol.sDebugTracing = false;
}