summaryrefslogtreecommitdiffstats
path: root/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
diff options
context:
space:
mode:
Diffstat (limited to 'quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java')
-rw-r--r--quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java208
1 files changed, 202 insertions, 6 deletions
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index d01b5ec19..db2af59ac 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -15,26 +15,102 @@
*/
package com.android.quickstep.inputconsumers;
+import static android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_POINTER_DOWN;
+import static android.view.MotionEvent.ACTION_UP;
+
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.Utilities.squaredTouchSlop;
+import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
+import static com.android.quickstep.WindowTransformSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.graphics.Point;
import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.RectF;
import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.ViewConfiguration;
+import android.view.WindowManager;
+
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.quickstep.LockScreenRecentsActivity;
+import com.android.quickstep.MultiStateCallback;
+import com.android.quickstep.SwipeSharedState;
+import com.android.quickstep.util.ClipAnimationHelper;
+import com.android.quickstep.util.RecentsAnimationListenerSet;
+import com.android.quickstep.util.SwipeAnimationTargetSet;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.BackgroundExecutor;
+import com.android.systemui.shared.system.InputMonitorCompat;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
/**
* A dummy input consumer used when the device is still locked, e.g. from secure camera.
*/
-public class DeviceLockedInputConsumer implements InputConsumer {
+public class DeviceLockedInputConsumer implements InputConsumer,
+ SwipeAnimationTargetSet.SwipeAnimationListener {
+
+ private static final float SCALE_DOWN = 0.75f;
+
+ private static final String[] STATE_NAMES = DEBUG_STATES ? new String[2] : null;
+ private static int getFlagForIndex(int index, String name) {
+ if (DEBUG_STATES) {
+ STATE_NAMES[index] = name;
+ }
+ return 1 << index;
+ }
+
+ private static final int STATE_TARGET_RECEIVED =
+ getFlagForIndex(0, "STATE_TARGET_RECEIVED");
+ private static final int STATE_HANDLER_INVALIDATED =
+ getFlagForIndex(1, "STATE_HANDLER_INVALIDATED");
private final Context mContext;
private final float mTouchSlopSquared;
+ private final SwipeSharedState mSwipeSharedState;
+ private final InputMonitorCompat mInputMonitorCompat;
+
private final PointF mTouchDown = new PointF();
+ private final ClipAnimationHelper mClipAnimationHelper;
+ private final ClipAnimationHelper.TransformParams mTransformParams;
+ private final Point mDisplaySize;
+ private final MultiStateCallback mStateCallback;
+ private final RectF mSwipeTouchRegion;
+ public final int mRunningTaskId;
+
+ private VelocityTracker mVelocityTracker;
+ private float mProgress;
- public DeviceLockedInputConsumer(Context context) {
+ private boolean mThresholdCrossed = false;
+
+ private SwipeAnimationTargetSet mTargetSet;
+
+ public DeviceLockedInputConsumer(Context context, SwipeSharedState swipeSharedState,
+ InputMonitorCompat inputMonitorCompat, RectF swipeTouchRegion, int runningTaskId) {
mContext = context;
mTouchSlopSquared = squaredTouchSlop(context);
+ mSwipeSharedState = swipeSharedState;
+ mClipAnimationHelper = new ClipAnimationHelper(context);
+ mTransformParams = new ClipAnimationHelper.TransformParams();
+ mInputMonitorCompat = inputMonitorCompat;
+ mSwipeTouchRegion = swipeTouchRegion;
+ mRunningTaskId = runningTaskId;
+
+ // Do not use DeviceProfile as the user data might be locked
+ mDisplaySize = new Point();
+ context.getSystemService(WindowManager.class).getDefaultDisplay().getRealSize(mDisplaySize);
+
+ // Init states
+ mStateCallback = new MultiStateCallback(STATE_NAMES);
+ mStateCallback.addCallback(STATE_TARGET_RECEIVED | STATE_HANDLER_INVALIDATED,
+ this::endRemoteAnimation);
+
+ mVelocityTracker = VelocityTracker.obtain();
}
@Override
@@ -44,17 +120,137 @@ public class DeviceLockedInputConsumer implements InputConsumer {
@Override
public void onMotionEvent(MotionEvent ev) {
+ if (mVelocityTracker == null) {
+ return;
+ }
+ mVelocityTracker.addMovement(ev);
+
float x = ev.getX();
float y = ev.getY();
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- mTouchDown.set(x, y);
- } else if (ev.getAction() == MotionEvent.ACTION_MOVE) {
- if (squaredHypot(x - mTouchDown.x, y - mTouchDown.y) > mTouchSlopSquared) {
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mTouchDown.set(x, y);
+ break;
+ case ACTION_POINTER_DOWN: {
+ if (!mThresholdCrossed) {
+ // Cancel interaction in case of multi-touch interaction
+ int ptrIdx = ev.getActionIndex();
+ if (!mSwipeTouchRegion.contains(ev.getX(ptrIdx), ev.getY(ptrIdx))) {
+ int action = ev.getAction();
+ ev.setAction(ACTION_CANCEL);
+ finishTouchTracking(ev);
+ ev.setAction(action);
+ }
+ }
+ break;
+ }
+ case MotionEvent.ACTION_MOVE: {
+ if (!mThresholdCrossed) {
+ if (squaredHypot(x - mTouchDown.x, y - mTouchDown.y) > mTouchSlopSquared) {
+ startRecentsTransition();
+ }
+ } else {
+ float dy = Math.max(mTouchDown.y - y, 0);
+ mProgress = dy / mDisplaySize.y;
+ mTransformParams.setProgress(mProgress);
+ if (mTargetSet != null) {
+ mClipAnimationHelper.applyTransform(mTargetSet, mTransformParams);
+ }
+ }
+ break;
+ }
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ finishTouchTracking(ev);
+ break;
+ }
+ }
+
+ /**
+ * Called when the gesture has ended. Does not correlate to the completion of the interaction as
+ * the animation can still be running.
+ */
+ private void finishTouchTracking(MotionEvent ev) {
+ mStateCallback.setState(STATE_HANDLER_INVALIDATED);
+ if (mThresholdCrossed && ev.getAction() == ACTION_UP) {
+ mVelocityTracker.computeCurrentVelocity(1000,
+ ViewConfiguration.get(mContext).getScaledMaximumFlingVelocity());
+
+ float velocityY = mVelocityTracker.getYVelocity();
+ float flingThreshold = mContext.getResources()
+ .getDimension(R.dimen.quickstep_fling_threshold_velocity);
+
+ boolean dismissTask;
+ if (Math.abs(velocityY) > flingThreshold) {
+ // Is fling
+ dismissTask = velocityY < 0;
+ } else {
+ dismissTask = mProgress >= (1 - MIN_PROGRESS_FOR_OVERVIEW);
+ }
+ if (dismissTask) {
// For now, just start the home intent so user is prompted to unlock the device.
mContext.startActivity(new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
}
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+
+ private void startRecentsTransition() {
+ mThresholdCrossed = true;
+ RecentsAnimationListenerSet newListenerSet =
+ mSwipeSharedState.newRecentsAnimationListenerSet();
+ newListenerSet.addListener(this);
+ Intent intent = new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .setComponent(new ComponentName(mContext, LockScreenRecentsActivity.class))
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+
+ mInputMonitorCompat.pilferPointers();
+ BackgroundExecutor.get().submit(
+ () -> ActivityManagerWrapper.getInstance().startRecentsActivity(
+ intent, null, newListenerSet, null, null));
+ }
+
+ @Override
+ public void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet) {
+ mTargetSet = targetSet;
+
+ Rect displaySize = new Rect(0, 0, mDisplaySize.x, mDisplaySize.y);
+ RemoteAnimationTargetCompat targetCompat = targetSet.findTask(mRunningTaskId);
+ if (targetCompat != null) {
+ mClipAnimationHelper.updateSource(displaySize, targetCompat);
+ }
+
+ Utilities.scaleRectAboutCenter(displaySize, SCALE_DOWN);
+ displaySize.offsetTo(displaySize.left, 0);
+ mClipAnimationHelper.updateTargetRect(displaySize);
+ mClipAnimationHelper.applyTransform(mTargetSet, mTransformParams);
+
+ mStateCallback.setState(STATE_TARGET_RECEIVED);
+ }
+
+ @Override
+ public void onRecentsAnimationCanceled() {
+ mTargetSet = null;
+ }
+
+ private void endRemoteAnimation() {
+ if (mTargetSet != null) {
+ mTargetSet.finishController(
+ false /* toRecents */, null /* callback */, false /* sendUserLeaveHint */);
+ }
+ }
+
+ @Override
+ public void onConsumerAboutToBeSwitched() {
+ mStateCallback.setState(STATE_HANDLER_INVALIDATED);
+ }
+
+ @Override
+ public boolean allowInterceptByParent() {
+ return !mThresholdCrossed;
}
}