diff options
author | Sunny Goyal <sunnygoyal@google.com> | 2019-06-24 16:01:13 -0700 |
---|---|---|
committer | Sunny Goyal <sunnygoyal@google.com> | 2019-06-25 13:25:30 -0700 |
commit | 700dc99d9b3d7df7e1910fa5661d60714a7f6a0d (patch) | |
tree | 46153ce762cfe17853aafbc4495ddbca3a85ea6e | |
parent | a90531cd00ad64fb8501c8190f99e068f9d581d6 (diff) | |
download | android_packages_apps_Trebuchet-700dc99d9b3d7df7e1910fa5661d60714a7f6a0d.tar.gz android_packages_apps_Trebuchet-700dc99d9b3d7df7e1910fa5661d60714a7f6a0d.tar.bz2 android_packages_apps_Trebuchet-700dc99d9b3d7df7e1910fa5661d60714a7f6a0d.zip |
Enabling simple gesture navigation for 3P launcher
In case of 3P launcher, swipe-up will go to Launcher and there will be
no way to reach overview.
Bug: 135769778
Change-Id: Ib2c6bb1b13e6055d30b7360ec077b0a2fece66ff
5 files changed, 392 insertions, 2 deletions
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 22ebe6140..8f08f0de9 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -81,6 +81,7 @@ import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener; import com.android.quickstep.inputconsumers.AccessibilityInputConsumer; import com.android.quickstep.inputconsumers.AssistantTouchConsumer; import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer; +import com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer; import com.android.quickstep.inputconsumers.InputConsumer; import com.android.quickstep.inputconsumers.OtherActivityInputConsumer; import com.android.quickstep.inputconsumers.OverviewInputConsumer; @@ -614,6 +615,10 @@ public class TouchInteractionService extends Service implements } else if (mGestureBlockingActivity != null && runningTaskInfo != null && mGestureBlockingActivity.equals(runningTaskInfo.topActivity)) { return mResetGestureInputConsumer; + } else if (mMode == Mode.NO_BUTTON && !mOverviewComponentObserver.isHomeAndOverviewSame()) { + return new FallbackNoButtonInputConsumer(this, activityControl, + mInputMonitorCompat, mSwipeSharedState, mSwipeTouchRegion, + mOverviewComponentObserver, disableHorizontalSwipe(event), runningTaskInfo); } else { return createOtherActivityInputConsumer(event, runningTaskInfo); } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java new file mode 100644 index 000000000..d05ca2a16 --- /dev/null +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java @@ -0,0 +1,358 @@ +/* + * 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.inputconsumers; + +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 static android.view.MotionEvent.INVALID_POINTER_ID; + +import static com.android.quickstep.WindowTransformSwipeHandler.MAX_SWIPE_DURATION; +import static com.android.quickstep.WindowTransformSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW; +import static com.android.quickstep.WindowTransformSwipeHandler.MIN_SWIPE_DURATION; +import static com.android.quickstep.inputconsumers.OtherActivityInputConsumer.QUICKSTEP_TOUCH_SLOP_RATIO; +import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.app.ActivityManager.RunningTaskInfo; +import android.content.Context; +import android.content.Intent; +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.DeviceProfile; +import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.R; +import com.android.quickstep.ActivityControlHelper; +import com.android.quickstep.OverviewComponentObserver; +import com.android.quickstep.SwipeSharedState; +import com.android.quickstep.util.ClipAnimationHelper; +import com.android.quickstep.util.ClipAnimationHelper.TransformParams; +import com.android.quickstep.util.NavBarPosition; +import com.android.quickstep.util.RecentsAnimationListenerSet; +import com.android.quickstep.util.SwipeAnimationTargetSet; +import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener; +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; + +public class FallbackNoButtonInputConsumer implements InputConsumer, SwipeAnimationListener { + + private static final int STATE_NOT_FINISHED = 0; + private static final int STATE_FINISHED_TO_HOME = 1; + private static final int STATE_FINISHED_TO_APP = 2; + + private static final float PROGRESS_TO_END_GESTURE = -2; + + private final ActivityControlHelper mActivityControlHelper; + private final InputMonitorCompat mInputMonitor; + private final Context mContext; + private final NavBarPosition mNavBarPosition; + private final SwipeSharedState mSwipeSharedState; + private final OverviewComponentObserver mOverviewComponentObserver; + private final int mRunningTaskId; + + private final ClipAnimationHelper mClipAnimationHelper; + private final TransformParams mTransformParams = new TransformParams(); + private final float mTransitionDragLength; + private final DeviceProfile mDP; + + private final RectF mSwipeTouchRegion; + private final boolean mDisableHorizontalSwipe; + + private final PointF mDownPos = new PointF(); + private final PointF mLastPos = new PointF(); + + private int mActivePointerId = -1; + // Slop used to determine when we say that the gesture has started. + private boolean mPassedPilferInputSlop; + + private VelocityTracker mVelocityTracker; + + // Distance after which we start dragging the window. + private final float mTouchSlop; + + // Might be displacement in X or Y, depending on the direction we are swiping from the nav bar. + private float mStartDisplacement; + private SwipeAnimationTargetSet mSwipeAnimationTargetSet; + private float mProgress; + + private int mState = STATE_NOT_FINISHED; + + public FallbackNoButtonInputConsumer(Context context, + ActivityControlHelper activityControlHelper, InputMonitorCompat inputMonitor, + SwipeSharedState swipeSharedState, RectF swipeTouchRegion, + OverviewComponentObserver overviewComponentObserver, + boolean disableHorizontalSwipe, RunningTaskInfo runningTaskInfo) { + mContext = context; + mActivityControlHelper = activityControlHelper; + mInputMonitor = inputMonitor; + mOverviewComponentObserver = overviewComponentObserver; + mRunningTaskId = runningTaskInfo.id; + + mSwipeSharedState = swipeSharedState; + mSwipeTouchRegion = swipeTouchRegion; + mDisableHorizontalSwipe = disableHorizontalSwipe; + + mNavBarPosition = new NavBarPosition(context); + mVelocityTracker = VelocityTracker.obtain(); + + mTouchSlop = QUICKSTEP_TOUCH_SLOP_RATIO + * ViewConfiguration.get(context).getScaledTouchSlop(); + + mClipAnimationHelper = new ClipAnimationHelper(context); + + mDP = InvariantDeviceProfile.INSTANCE.get(context).getDeviceProfile(context).copy(context); + Rect tempRect = new Rect(); + mTransitionDragLength = mActivityControlHelper.getSwipeUpDestinationAndLength( + mDP, context, tempRect); + mClipAnimationHelper.updateTargetRect(tempRect); + } + + @Override + public int getType() { + return TYPE_FALLBACK_NO_BUTTON; + } + + @Override + public void onMotionEvent(MotionEvent ev) { + if (mVelocityTracker == null) { + return; + } + + mVelocityTracker.addMovement(ev); + if (ev.getActionMasked() == ACTION_POINTER_UP) { + mVelocityTracker.clear(); + } + + switch (ev.getActionMasked()) { + case ACTION_DOWN: { + mActivePointerId = ev.getPointerId(0); + mDownPos.set(ev.getX(), ev.getY()); + mLastPos.set(mDownPos); + break; + } + case ACTION_POINTER_DOWN: { + if (!mPassedPilferInputSlop) { + // Cancel interaction in case of multi-touch interaction + int ptrIdx = ev.getActionIndex(); + if (!mSwipeTouchRegion.contains(ev.getX(ptrIdx), ev.getY(ptrIdx))) { + forceCancelGesture(ev); + } + } + break; + } + case ACTION_POINTER_UP: { + int ptrIdx = ev.getActionIndex(); + int ptrId = ev.getPointerId(ptrIdx); + if (ptrId == mActivePointerId) { + final int newPointerIdx = ptrIdx == 0 ? 1 : 0; + mDownPos.set( + ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x), + ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y)); + mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx)); + mActivePointerId = ev.getPointerId(newPointerIdx); + } + break; + } + case ACTION_MOVE: { + int pointerIndex = ev.findPointerIndex(mActivePointerId); + if (pointerIndex == INVALID_POINTER_ID) { + break; + } + mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex)); + float displacement = getDisplacement(ev); + + if (!mPassedPilferInputSlop) { + if (mDisableHorizontalSwipe && Math.abs(mLastPos.x - mDownPos.x) + > Math.abs(mLastPos.y - mDownPos.y)) { + // Horizontal gesture is not allowed in this region + forceCancelGesture(ev); + break; + } + + if (Math.abs(displacement) >= mTouchSlop) { + mPassedPilferInputSlop = true; + + // Deferred gesture, start the animation and gesture tracking once + // we pass the actual touch slop + startTouchTrackingForWindowAnimation(displacement); + } + } else { + updateDisplacement(displacement - mStartDisplacement); + } + break; + } + case ACTION_CANCEL: + case ACTION_UP: { + finishTouchTracking(ev); + break; + } + } + } + + private void startTouchTrackingForWindowAnimation(float displacement) { + mStartDisplacement = Math.min(displacement, -mTouchSlop); + + RecentsAnimationListenerSet listenerSet = + mSwipeSharedState.newRecentsAnimationListenerSet(); + listenerSet.addListener(this); + Intent homeIntent = mOverviewComponentObserver.getHomeIntent(); + BackgroundExecutor.get().submit( + () -> ActivityManagerWrapper.getInstance().startRecentsActivity( + homeIntent, null, listenerSet, null, null)); + + ActivityManagerWrapper.getInstance().closeSystemWindows( + CLOSE_SYSTEM_WINDOWS_REASON_RECENTS); + mInputMonitor.pilferPointers(); + } + + private void updateDisplacement(float displacement) { + mProgress = displacement / mTransitionDragLength; + mTransformParams.setProgress(mProgress); + + if (mSwipeAnimationTargetSet != null) { + mClipAnimationHelper.applyTransform(mSwipeAnimationTargetSet, mTransformParams); + } + } + + private void forceCancelGesture(MotionEvent ev) { + int action = ev.getAction(); + ev.setAction(ACTION_CANCEL); + finishTouchTracking(ev); + ev.setAction(action); + } + + /** + * 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) { + if (ev.getAction() == ACTION_CANCEL) { + mState = STATE_FINISHED_TO_APP; + } else { + mVelocityTracker.computeCurrentVelocity(1000, + ViewConfiguration.get(mContext).getScaledMaximumFlingVelocity()); + float velocityX = mVelocityTracker.getXVelocity(mActivePointerId); + float velocityY = mVelocityTracker.getYVelocity(mActivePointerId); + float velocity = mNavBarPosition.isRightEdge() ? velocityX + : mNavBarPosition.isLeftEdge() ? -velocityX + : velocityY; + float flingThreshold = mContext.getResources() + .getDimension(R.dimen.quickstep_fling_threshold_velocity); + boolean isFling = Math.abs(velocity) > flingThreshold; + + boolean goingHome; + if (!isFling) { + goingHome = -mProgress >= MIN_PROGRESS_FOR_OVERVIEW; + } else { + goingHome = velocity < 0; + } + + if (goingHome) { + mState = STATE_FINISHED_TO_HOME; + } else { + mState = STATE_FINISHED_TO_APP; + } + } + + if (mSwipeAnimationTargetSet != null) { + finishAnimationTargetSet(); + } + } + + private void finishAnimationTargetSet() { + if (mState == STATE_FINISHED_TO_APP) { + mSwipeAnimationTargetSet.finishController(false, null, false); + } else { + if (mProgress < PROGRESS_TO_END_GESTURE) { + mSwipeAnimationTargetSet.finishController(true, null, true); + } else { + long duration = (long) (Math.min(mProgress - PROGRESS_TO_END_GESTURE, 1) + * MAX_SWIPE_DURATION / Math.abs(PROGRESS_TO_END_GESTURE)); + if (duration < 0) { + duration = MIN_SWIPE_DURATION; + } + + ValueAnimator anim = ValueAnimator.ofFloat(mProgress, PROGRESS_TO_END_GESTURE); + anim.addUpdateListener(a -> { + float p = (Float) anim.getAnimatedValue(); + mTransformParams.setProgress(p); + mClipAnimationHelper.applyTransform(mSwipeAnimationTargetSet, mTransformParams); + }); + anim.setDuration(duration); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mSwipeAnimationTargetSet.finishController(true, null, true); + } + }); + anim.start(); + } + } + } + + @Override + public void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet) { + mSwipeAnimationTargetSet = targetSet; + Rect overviewStackBounds = new Rect(0, 0, mDP.widthPx, mDP.heightPx); + RemoteAnimationTargetCompat runningTaskTarget = targetSet.findTask(mRunningTaskId); + + mDP.updateIsSeascape(mContext.getSystemService(WindowManager.class)); + if (runningTaskTarget != null) { + mClipAnimationHelper.updateSource(overviewStackBounds, runningTaskTarget); + } + mClipAnimationHelper.prepareAnimation(mDP, false /* isOpening */); + + overviewStackBounds + .inset(-overviewStackBounds.width() / 5, -overviewStackBounds.height() / 5); + mClipAnimationHelper.updateTargetRect(overviewStackBounds); + mClipAnimationHelper.applyTransform(mSwipeAnimationTargetSet, mTransformParams); + + if (mState != STATE_NOT_FINISHED) { + finishAnimationTargetSet(); + } + } + + @Override + public void onRecentsAnimationCanceled() { } + + private float getDisplacement(MotionEvent ev) { + if (mNavBarPosition.isRightEdge()) { + return ev.getX() - mDownPos.x; + } else if (mNavBarPosition.isLeftEdge()) { + return mDownPos.x - ev.getX(); + } else { + return ev.getY() - mDownPos.y; + } + } + + @Override + public boolean allowInterceptByParent() { + return !mPassedPilferInputSlop; + } +} diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java index a1e5d47a5..f5cf654b1 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java @@ -33,6 +33,7 @@ public interface InputConsumer { int TYPE_SCREEN_PINNED = 1 << 6; int TYPE_OVERVIEW_WITHOUT_FOCUS = 1 << 7; int TYPE_RESET_GESTURE = 1 << 8; + int TYPE_FALLBACK_NO_BUTTON = 1 << 9; String[] NAMES = new String[] { "TYPE_NO_OP", // 0 @@ -44,6 +45,7 @@ public interface InputConsumer { "TYPE_SCREEN_PINNED", // 6 "TYPE_OVERVIEW_WITHOUT_FOCUS", // 7 "TYPE_RESET_GESTURE", // 8 + "TYPE_FALLBACK_NO_BUTTON", // 9 }; InputConsumer NO_OP = () -> TYPE_NO_OP; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java index 6bc543f07..4c137d3bf 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java @@ -79,7 +79,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC private static final String UP_EVT = "OtherActivityInputConsumer.UP"; // TODO: Move to quickstep contract - private static final float QUICKSTEP_TOUCH_SLOP_RATIO = 3; + public static final float QUICKSTEP_TOUCH_SLOP_RATIO = 3; private final CachedEventDispatcher mRecentsViewDispatcher = new CachedEventDispatcher(); private final RunningTaskInfo mRunningTask; diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java index 0a73b8b19..0738affa9 100644 --- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java +++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java @@ -33,7 +33,6 @@ import android.content.pm.ResolveInfo; import com.android.systemui.shared.system.PackageManagerWrapper; -import com.android.systemui.shared.system.QuickStepContract; import java.util.ArrayList; /** @@ -58,7 +57,9 @@ public final class OverviewComponentObserver { private String mUpdateRegisteredPackage; private ActivityControlHelper mActivityControlHelper; private Intent mOverviewIntent; + private Intent mHomeIntent; private int mSystemUiStateFlags; + private boolean mIsHomeAndOverviewSame; public OverviewComponentObserver(Context context) { mContext = context; @@ -93,11 +94,14 @@ public final class OverviewComponentObserver { final String overviewIntentCategory; ComponentName overviewComponent; + mHomeIntent = null; + if ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0 && (defaultHome == null || mMyHomeComponent.equals(defaultHome))) { // User default home is same as out home app. Use Overview integrated in Launcher. overviewComponent = mMyHomeComponent; mActivityControlHelper = new LauncherActivityControllerHelper(); + mIsHomeAndOverviewSame = true; overviewIntentCategory = Intent.CATEGORY_HOME; if (mUpdateRegisteredPackage != null) { @@ -109,8 +113,12 @@ public final class OverviewComponentObserver { // The default home app is a different launcher. Use the fallback Overview instead. overviewComponent = new ComponentName(mContext, RecentsActivity.class); mActivityControlHelper = new FallbackActivityControllerHelper(); + mIsHomeAndOverviewSame = false; overviewIntentCategory = Intent.CATEGORY_DEFAULT; + mHomeIntent = new Intent(Intent.ACTION_MAIN) + .addCategory(Intent.CATEGORY_HOME) + .setComponent(defaultHome); // User's default home app can change as a result of package updates of this app (such // as uninstalling the app or removing the "Launcher" feature in an update). // Listen for package updates of this app (and remove any previously attached @@ -135,6 +143,9 @@ public final class OverviewComponentObserver { .addCategory(overviewIntentCategory) .setComponent(overviewComponent) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + if (mHomeIntent == null) { + mHomeIntent = mOverviewIntent; + } } /** @@ -159,6 +170,20 @@ public final class OverviewComponentObserver { } /** + * Get the current intent for going to the home activity. + */ + public Intent getHomeIntent() { + return mHomeIntent; + } + + /** + * Returns true if home and overview are same activity. + */ + public boolean isHomeAndOverviewSame() { + return mIsHomeAndOverviewSame; + } + + /** * Get the current activity control helper for managing interactions to the overview activity. * * @return the current activity control helper |