diff options
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.java | 208 |
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; } } |