blob: e2747dfcbb6a307d414cf01edee73a30c84362bd [file] [log] [blame]
/*
* Copyright (C) 2020 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.launcher3.uioverrides.touchcontrollers;
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL_APPS_EDU;
import static com.android.launcher3.AbstractFloatingView.getOpenView;
import static com.android.launcher3.LauncherState.HINT_STATE_TWO_BUTTON;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import android.animation.ValueAnimator;
import android.os.SystemClock;
import android.util.Log;
import android.view.MotionEvent;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.AbstractStateChangeTouchController;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.views.AllAppsEduView;
/**
* Touch controller for handling edge swipes in 2-button mode
*/
public class TwoButtonNavbarTouchController extends AbstractStateChangeTouchController {
private static final int MAX_NUM_SWIPES_TO_TRIGGER_EDU = 3;
private static final String TAG = "2BtnNavbarTouchCtrl";
private final boolean mIsTransposed;
// If true, we will finish the current animation instantly on second touch.
private boolean mFinishFastOnSecondTouch;
private int mContinuousTouchCount = 0;
public TwoButtonNavbarTouchController(Launcher l) {
super(l, l.getDeviceProfile().isVerticalBarLayout()
? SingleAxisSwipeDetector.HORIZONTAL : SingleAxisSwipeDetector.VERTICAL);
mIsTransposed = l.getDeviceProfile().isVerticalBarLayout();
}
@Override
protected boolean canInterceptTouch(MotionEvent ev) {
boolean canIntercept = canInterceptTouchInternal(ev);
if (!canIntercept) {
mContinuousTouchCount = 0;
}
return canIntercept;
}
private boolean canInterceptTouchInternal(MotionEvent ev) {
if (mCurrentAnimation != null) {
if (mFinishFastOnSecondTouch) {
mCurrentAnimation.getAnimationPlayer().end();
}
// If we are already animating from a previous state, we can intercept.
return true;
}
if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
return false;
}
if ((ev.getEdgeFlags() & EDGE_NAV_BAR) == 0) {
return false;
}
if (!mIsTransposed && mLauncher.isInState(OVERVIEW)) {
return true;
}
return mLauncher.isInState(NORMAL);
}
@Override
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
boolean intercept = super.onControllerInterceptTouchEvent(ev);
return intercept;
}
@Override
protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
if (mIsTransposed) {
boolean draggingFromNav =
mLauncher.getDeviceProfile().isSeascape() == isDragTowardPositive;
return draggingFromNav ? HINT_STATE_TWO_BUTTON : NORMAL;
} else {
LauncherState startState = mStartState != null ? mStartState : fromState;
return isDragTowardPositive ^ (startState == OVERVIEW) ? HINT_STATE_TWO_BUTTON : NORMAL;
}
}
@Override
protected void onReinitToState(LauncherState newToState) {
super.onReinitToState(newToState);
}
@Override
protected void updateSwipeCompleteAnimation(ValueAnimator animator, long expectedDuration,
LauncherState targetState, float velocity, boolean isFling) {
super.updateSwipeCompleteAnimation(animator, expectedDuration, targetState,
velocity, isFling);
mFinishFastOnSecondTouch = !mIsTransposed && mFromState == NORMAL;
if (targetState == HINT_STATE_TWO_BUTTON) {
// We were going to HINT_STATE_TWO_BUTTON, but end that animation immediately so we go
// to OVERVIEW instead.
animator.setDuration(0);
}
}
@Override
protected float getShiftRange() {
// Should be in sync with TestProtocol.REQUEST_HOME_TO_OVERVIEW_SWIPE_HEIGHT
return LayoutUtils.getDefaultSwipeHeight(mLauncher, mLauncher.getDeviceProfile());
}
@Override
protected float initCurrentAnimation() {
float range = getShiftRange();
long maxAccuracy = (long) (2 * range);
mCurrentAnimation = mLauncher.getStateManager().createAnimationToNewWorkspace(mToState,
maxAccuracy);
return (mLauncher.getDeviceProfile().isSeascape() ? 1 : -1) / range;
}
@Override
protected void updateProgress(float fraction) {
super.updateProgress(fraction);
// We have reached HINT_STATE, end the gesture now to go to OVERVIEW.
if (fraction >= 1 && mToState == HINT_STATE_TWO_BUTTON) {
final long now = SystemClock.uptimeMillis();
MotionEvent event = MotionEvent.obtain(now, now,
MotionEvent.ACTION_UP, 0.0f, 0.0f, 0);
mDetector.onTouchEvent(event);
event.recycle();
}
}
@Override
protected void onSwipeInteractionCompleted(LauncherState targetState) {
super.onSwipeInteractionCompleted(targetState);
if (!mIsTransposed) {
mContinuousTouchCount++;
}
if (mStartState == NORMAL && targetState == HINT_STATE_TWO_BUTTON) {
SystemUiProxy.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
} else if (targetState == NORMAL
&& mContinuousTouchCount >= MAX_NUM_SWIPES_TO_TRIGGER_EDU) {
mContinuousTouchCount = 0;
if (getOpenView(mLauncher, TYPE_ALL_APPS_EDU) == null) {
AllAppsEduView.show(mLauncher);
}
}
mStartState = null;
}
}