Pinch to zoom out into overview mode.
am: dadb304b46
* commit 'dadb304b4682998c43b9c07bef45b9a9380f3287':
Pinch to zoom out into overview mode.
This commit is contained in:
@@ -3136,6 +3136,7 @@ public class Launcher extends Activity
|
||||
getDragLayer().sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
if (!isDraggingEnabled()) return false;
|
||||
if (isWorkspaceLocked()) return false;
|
||||
|
||||
@@ -1445,6 +1445,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
|
||||
mLastMotionXRemainder = 0;
|
||||
onScrollInteractionBegin();
|
||||
pageBeginMoving();
|
||||
// Stop listening for things like pinches.
|
||||
requestDisallowInterceptTouchEvent(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,285 @@
|
||||
package com.android.launcher3;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import static com.android.launcher3.Workspace.State.NORMAL;
|
||||
import static com.android.launcher3.Workspace.State.OVERVIEW;
|
||||
|
||||
/**
|
||||
* Manages the animations that play as the user pinches to/from overview mode.
|
||||
*
|
||||
* It will look like this pinching in:
|
||||
* - Workspace scales down
|
||||
* - At some threshold 1, hotseat and QSB fade out (full animation)
|
||||
* - At a later threshold 2, panel buttons fade in and scrim fades in
|
||||
* - At a final threshold 3, snap to overview
|
||||
*
|
||||
* Pinching out:
|
||||
* - Workspace scales up
|
||||
* - At threshold 1, panel buttons fade out
|
||||
* - At threshold 2, hotseat and QSB fade in and scrim fades out
|
||||
* - At threshold 3, snap to workspace
|
||||
*
|
||||
* @see PinchToOverviewListener
|
||||
* @see PinchThresholdManager
|
||||
*/
|
||||
public class PinchAnimationManager {
|
||||
private static final String TAG = "PinchAnimationManager";
|
||||
|
||||
private static final int THRESHOLD_ANIM_DURATION = 150;
|
||||
|
||||
private Launcher mLauncher;
|
||||
private Workspace mWorkspace;
|
||||
|
||||
private float mOverviewScale;
|
||||
private float mOverviewTranslationY;
|
||||
private int mNormalOverviewTransitionDuration;
|
||||
private final int[] mVisiblePageRange = new int[2];
|
||||
private boolean mIsAnimating;
|
||||
|
||||
// Animators
|
||||
private Animator mShowPageIndicatorAnimator;
|
||||
private Animator mShowHotseatAnimator;
|
||||
private Animator mShowOverviewPanelButtonsAnimator;
|
||||
private Animator mShowScrimAnimator;
|
||||
private Animator mHidePageIndicatorAnimator;
|
||||
private Animator mHideHotseatAnimator;
|
||||
private Animator mHideOverviewPanelButtonsAnimator;
|
||||
private Animator mHideScrimAnimator;
|
||||
|
||||
public PinchAnimationManager(Launcher launcher) {
|
||||
mLauncher = launcher;
|
||||
mWorkspace = launcher.mWorkspace;
|
||||
|
||||
mOverviewScale = mWorkspace.getOverviewModeShrinkFactor();
|
||||
mOverviewTranslationY = mWorkspace.getOverviewModeTranslationY();
|
||||
mNormalOverviewTransitionDuration = mWorkspace.getStateTransitionAnimation()
|
||||
.mOverviewTransitionTime;
|
||||
|
||||
initializeAnimators();
|
||||
}
|
||||
|
||||
private void initializeAnimators() {
|
||||
mShowPageIndicatorAnimator = new LauncherViewPropertyAnimator(
|
||||
mWorkspace.getPageIndicator()).alpha(1f).withLayer();
|
||||
mShowPageIndicatorAnimator.setInterpolator(null);
|
||||
|
||||
mShowHotseatAnimator = new LauncherViewPropertyAnimator(mLauncher.getHotseat())
|
||||
.alpha(1f).withLayer();
|
||||
mShowHotseatAnimator.setInterpolator(null);
|
||||
|
||||
mShowOverviewPanelButtonsAnimator = new LauncherViewPropertyAnimator(
|
||||
mLauncher.getOverviewPanel()).alpha(1f).withLayer();
|
||||
mShowOverviewPanelButtonsAnimator.setInterpolator(null);
|
||||
|
||||
mShowScrimAnimator = ObjectAnimator.ofFloat(mLauncher.getDragLayer(), "backgroundAlpha",
|
||||
mWorkspace.getStateTransitionAnimation().mWorkspaceScrimAlpha);
|
||||
mShowScrimAnimator.setInterpolator(null);
|
||||
|
||||
mHidePageIndicatorAnimator = new LauncherViewPropertyAnimator(
|
||||
mWorkspace.getPageIndicator()).alpha(0f).withLayer();
|
||||
mHidePageIndicatorAnimator.setInterpolator(null);
|
||||
mHidePageIndicatorAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (mWorkspace.getPageIndicator() != null) {
|
||||
mWorkspace.getPageIndicator().setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mHideHotseatAnimator = new LauncherViewPropertyAnimator(mLauncher.getHotseat())
|
||||
.alpha(0f).withLayer();
|
||||
mHideHotseatAnimator.setInterpolator(null);
|
||||
mHideHotseatAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mLauncher.getHotseat().setVisibility(View.INVISIBLE);
|
||||
}
|
||||
});
|
||||
|
||||
mHideOverviewPanelButtonsAnimator = new LauncherViewPropertyAnimator(
|
||||
mLauncher.getOverviewPanel()).alpha(0f).withLayer();
|
||||
mHideOverviewPanelButtonsAnimator.setInterpolator(null);
|
||||
mHideOverviewPanelButtonsAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mLauncher.getOverviewPanel().setVisibility(View.INVISIBLE);
|
||||
}
|
||||
});
|
||||
|
||||
mHideScrimAnimator = ObjectAnimator.ofFloat(mLauncher.getDragLayer(), "backgroundAlpha", 0f);
|
||||
mHideScrimAnimator.setInterpolator(null);
|
||||
}
|
||||
|
||||
public int getNormalOverviewTransitionDuration() {
|
||||
return mNormalOverviewTransitionDuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpolate from {@param currentProgress} to {@param toProgress}, calling
|
||||
* {@link #setAnimationProgress(float)} throughout the duration. If duration is -1,
|
||||
* the default overview transition duration is used.
|
||||
*/
|
||||
public void animateToProgress(float currentProgress, float toProgress, int duration,
|
||||
final PinchThresholdManager thresholdManager) {
|
||||
if (duration == -1) {
|
||||
duration = mNormalOverviewTransitionDuration;
|
||||
}
|
||||
ValueAnimator animator = ValueAnimator.ofFloat(currentProgress, toProgress);
|
||||
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
float pinchProgress = (Float) animation.getAnimatedValue();
|
||||
setAnimationProgress(pinchProgress);
|
||||
thresholdManager.updateAndAnimatePassedThreshold(pinchProgress,
|
||||
PinchAnimationManager.this);
|
||||
}
|
||||
}
|
||||
);
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mIsAnimating = false;
|
||||
thresholdManager.reset();
|
||||
}
|
||||
});
|
||||
animator.setDuration(duration).start();
|
||||
mIsAnimating = true;
|
||||
}
|
||||
|
||||
public boolean isAnimating() {
|
||||
return mIsAnimating;
|
||||
}
|
||||
|
||||
/**
|
||||
* Animates to the specified progress. This should be called repeatedly throughout the pinch
|
||||
* gesture to run animations that interpolate throughout the gesture.
|
||||
* @param interpolatedProgress The progress from 0 to 1, where 0 is overview and 1 is workspace.
|
||||
*/
|
||||
public void setAnimationProgress(float interpolatedProgress) {
|
||||
float interpolatedScale = interpolatedProgress * (1f - mOverviewScale) + mOverviewScale;
|
||||
float interpolatedTranslationY = (1f - interpolatedProgress) * mOverviewTranslationY;
|
||||
mWorkspace.setScaleX(interpolatedScale);
|
||||
mWorkspace.setScaleY(interpolatedScale);
|
||||
mWorkspace.setTranslationY(interpolatedTranslationY);
|
||||
setOverviewPanelsAlpha(1f - interpolatedProgress, 0);
|
||||
|
||||
// Make sure adjacent pages, except custom content page, are visible while scaling.
|
||||
mWorkspace.setCustomContentVisibility(View.INVISIBLE);
|
||||
mWorkspace.invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Animates certain properties based on which threshold was passed, and in what direction. The
|
||||
* starting state must also be taken into account because the thresholds mean different things
|
||||
* when going from workspace to overview and vice versa.
|
||||
* @param threshold One of {@link PinchThresholdManager#THRESHOLD_ONE},
|
||||
* {@link PinchThresholdManager#THRESHOLD_TWO}, or
|
||||
* {@link PinchThresholdManager#THRESHOLD_THREE}
|
||||
* @param startState {@link Workspace.State#NORMAL} or {@link Workspace.State#OVERVIEW}.
|
||||
* @param goingTowards {@link Workspace.State#NORMAL} or {@link Workspace.State#OVERVIEW}.
|
||||
* Note that this doesn't have to be the opposite of startState;
|
||||
*/
|
||||
public void animateThreshold(float threshold, Workspace.State startState,
|
||||
Workspace.State goingTowards) {
|
||||
if (threshold == PinchThresholdManager.THRESHOLD_ONE) {
|
||||
if (startState == OVERVIEW) {
|
||||
animateOverviewPanelButtons(goingTowards == OVERVIEW);
|
||||
} else if (startState == NORMAL) {
|
||||
animateHotseatAndPageIndicator(goingTowards == NORMAL);
|
||||
animateQsb(goingTowards == NORMAL);
|
||||
}
|
||||
} else if (threshold == PinchThresholdManager.THRESHOLD_TWO) {
|
||||
if (startState == OVERVIEW) {
|
||||
animateHotseatAndPageIndicator(goingTowards == NORMAL);
|
||||
animateQsb(goingTowards == NORMAL);
|
||||
animateScrim(goingTowards == OVERVIEW);
|
||||
} else if (startState == NORMAL) {
|
||||
animateOverviewPanelButtons(goingTowards == OVERVIEW);
|
||||
animateScrim(goingTowards == OVERVIEW);
|
||||
}
|
||||
} else if (threshold == PinchThresholdManager.THRESHOLD_THREE) {
|
||||
// Passing threshold 3 ends the pinch and snaps to the new state.
|
||||
if (startState == OVERVIEW && goingTowards == NORMAL) {
|
||||
mLauncher.showWorkspace(true);
|
||||
mWorkspace.snapToPage(mWorkspace.getPageNearestToCenterOfScreen());
|
||||
} else if (startState == NORMAL && goingTowards == OVERVIEW) {
|
||||
mLauncher.showOverviewMode(true);
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "Received unknown threshold to animate: " + threshold);
|
||||
}
|
||||
}
|
||||
|
||||
private void setOverviewPanelsAlpha(float alpha, int duration) {
|
||||
mWorkspace.getVisiblePages(mVisiblePageRange);
|
||||
for (int i = mVisiblePageRange[0]; i <= mVisiblePageRange[1]; i++) {
|
||||
View page = mWorkspace.getPageAt(i);
|
||||
if (!mWorkspace.shouldDrawChild(page)) {
|
||||
continue;
|
||||
}
|
||||
if (duration == 0) {
|
||||
((CellLayout) page).setBackgroundAlpha(alpha);
|
||||
} else {
|
||||
ObjectAnimator.ofFloat(page, "backgroundAlpha", alpha)
|
||||
.setDuration(duration).start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void animateHotseatAndPageIndicator(boolean show) {
|
||||
if (show) {
|
||||
mLauncher.getHotseat().setVisibility(View.VISIBLE);
|
||||
mShowHotseatAnimator.setDuration(THRESHOLD_ANIM_DURATION).start();
|
||||
if (mWorkspace.getPageIndicator() != null) {
|
||||
// There aren't page indicators in landscape mode on phones, hence the null check.
|
||||
mWorkspace.getPageIndicator().setVisibility(View.VISIBLE);
|
||||
mShowPageIndicatorAnimator.setDuration(THRESHOLD_ANIM_DURATION).start();
|
||||
}
|
||||
} else {
|
||||
mHideHotseatAnimator.setDuration(THRESHOLD_ANIM_DURATION).start();
|
||||
if (mWorkspace.getPageIndicator() != null) {
|
||||
// There aren't page indicators in landscape mode on phones, hence the null check.
|
||||
mHidePageIndicatorAnimator.setDuration(THRESHOLD_ANIM_DURATION).start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void animateQsb(boolean show) {
|
||||
SearchDropTargetBar.State searchBarState = show ? SearchDropTargetBar.State.SEARCH_BAR
|
||||
: SearchDropTargetBar.State.INVISIBLE;
|
||||
mLauncher.getSearchDropTargetBar().animateToState(searchBarState, THRESHOLD_ANIM_DURATION);
|
||||
}
|
||||
|
||||
private void animateOverviewPanelButtons(boolean show) {
|
||||
if (show) {
|
||||
mLauncher.getOverviewPanel().setVisibility(View.VISIBLE);
|
||||
mShowOverviewPanelButtonsAnimator.setDuration(THRESHOLD_ANIM_DURATION).start();
|
||||
} else {
|
||||
mHideOverviewPanelButtonsAnimator.setDuration(THRESHOLD_ANIM_DURATION).start();
|
||||
}
|
||||
}
|
||||
|
||||
private void animateScrim(boolean show) {
|
||||
// We reninitialize the animators here so that they have the correct start values.
|
||||
if (show) {
|
||||
mShowScrimAnimator = ObjectAnimator.ofFloat(mLauncher.getDragLayer(), "backgroundAlpha",
|
||||
mWorkspace.getStateTransitionAnimation().mWorkspaceScrimAlpha);
|
||||
mShowScrimAnimator.setInterpolator(null);
|
||||
mShowScrimAnimator.setDuration(mNormalOverviewTransitionDuration).start();
|
||||
} else {
|
||||
mHideScrimAnimator.setupStartValues();
|
||||
mHideScrimAnimator = ObjectAnimator.ofFloat(mLauncher.getDragLayer(), "backgroundAlpha",
|
||||
0f);
|
||||
mHideScrimAnimator.setInterpolator(null);
|
||||
mHideScrimAnimator.setDuration(mNormalOverviewTransitionDuration).start();
|
||||
mHideScrimAnimator.setDuration(mNormalOverviewTransitionDuration).start();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.android.launcher3;
|
||||
|
||||
/**
|
||||
* Keeps track of when thresholds are passed during a pinch gesture,
|
||||
* used to inform {@link PinchAnimationManager} throughout.
|
||||
*
|
||||
* @see PinchToOverviewListener
|
||||
* @see PinchAnimationManager
|
||||
*/
|
||||
public class PinchThresholdManager {
|
||||
public static final float THRESHOLD_ZERO = 0.0f;
|
||||
public static final float THRESHOLD_ONE = 0.40f;
|
||||
public static final float THRESHOLD_TWO = 0.70f;
|
||||
public static final float THRESHOLD_THREE = 0.95f;
|
||||
|
||||
private Workspace mWorkspace;
|
||||
|
||||
private float mPassedThreshold = THRESHOLD_ZERO;
|
||||
|
||||
public PinchThresholdManager(Workspace workspace) {
|
||||
mWorkspace = workspace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the pinch progress to determine whether a threshold has been passed,
|
||||
* and asks the {@param animationManager} to animate if so.
|
||||
* @param progress From 0 to 1, where 0 is overview and 1 is workspace.
|
||||
* @param animationManager Animates the threshold change if one is passed.
|
||||
* @return The last passed threshold, one of
|
||||
* {@link PinchThresholdManager#THRESHOLD_ZERO},
|
||||
* {@link PinchThresholdManager#THRESHOLD_ONE},
|
||||
* {@link PinchThresholdManager#THRESHOLD_TWO}, or
|
||||
* {@link PinchThresholdManager#THRESHOLD_THREE}
|
||||
*/
|
||||
public float updateAndAnimatePassedThreshold(float progress,
|
||||
PinchAnimationManager animationManager) {
|
||||
if (!mWorkspace.isInOverviewMode()) {
|
||||
// Invert the progress, because going from workspace to overview is 1 to 0.
|
||||
progress = 1f - progress;
|
||||
}
|
||||
|
||||
float previousPassedThreshold = mPassedThreshold;
|
||||
|
||||
if (progress < THRESHOLD_ONE) {
|
||||
mPassedThreshold = THRESHOLD_ZERO;
|
||||
} else if (progress < THRESHOLD_TWO) {
|
||||
mPassedThreshold = THRESHOLD_ONE;
|
||||
} else if (progress < THRESHOLD_THREE) {
|
||||
mPassedThreshold = THRESHOLD_TWO;
|
||||
} else {
|
||||
mPassedThreshold = THRESHOLD_THREE;
|
||||
}
|
||||
|
||||
if (mPassedThreshold != previousPassedThreshold) {
|
||||
Workspace.State fromState = mWorkspace.isInOverviewMode() ? Workspace.State.OVERVIEW
|
||||
: Workspace.State.NORMAL;
|
||||
Workspace.State toState = mWorkspace.isInOverviewMode() ? Workspace.State.NORMAL
|
||||
: Workspace.State.OVERVIEW;
|
||||
float thresholdToAnimate = mPassedThreshold;
|
||||
if (mPassedThreshold < previousPassedThreshold) {
|
||||
// User reversed pinch, so heading back to the state that they started from.
|
||||
toState = fromState;
|
||||
thresholdToAnimate = previousPassedThreshold;
|
||||
}
|
||||
animationManager.animateThreshold(thresholdToAnimate, fromState, toState);
|
||||
}
|
||||
return mPassedThreshold;
|
||||
}
|
||||
|
||||
public float getPassedThreshold() {
|
||||
return mPassedThreshold;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
mPassedThreshold = THRESHOLD_ZERO;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
package com.android.launcher3;
|
||||
|
||||
import android.animation.TimeInterpolator;
|
||||
import android.content.Context;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ScaleGestureDetector;
|
||||
|
||||
/**
|
||||
* Detects pinches and animates the Workspace to/from overview mode.
|
||||
*
|
||||
* Usage: Pass MotionEvents to onInterceptTouchEvent() and onTouchEvent(). This class will handle
|
||||
* the pinch detection, and use {@link PinchAnimationManager} to handle the animations.
|
||||
*
|
||||
* @see PinchThresholdManager
|
||||
* @see PinchAnimationManager
|
||||
*/
|
||||
public class PinchToOverviewListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
|
||||
private static final float OVERVIEW_PROGRESS = 0f;
|
||||
private static final float WORKSPACE_PROGRESS = 1f;
|
||||
|
||||
private ScaleGestureDetector mPinchDetector;
|
||||
private Launcher mLauncher;
|
||||
private Workspace mWorkspace = null;
|
||||
private boolean mPinchStarted = false;
|
||||
private float mPreviousProgress;
|
||||
private float mProgressDelta;
|
||||
private long mPreviousTimeMillis;
|
||||
private long mTimeDelta;
|
||||
private boolean mPinchCanceled = false;
|
||||
private TimeInterpolator mInterpolator;
|
||||
|
||||
private PinchThresholdManager mThresholdManager;
|
||||
private PinchAnimationManager mAnimationManager;
|
||||
|
||||
public PinchToOverviewListener(Launcher launcher) {
|
||||
mLauncher = launcher;
|
||||
mPinchDetector = new ScaleGestureDetector((Context) mLauncher, this);
|
||||
}
|
||||
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
mPinchDetector.onTouchEvent(ev);
|
||||
return mPinchStarted;
|
||||
}
|
||||
|
||||
public void onTouchEvent(MotionEvent ev) {
|
||||
if (mPinchStarted) {
|
||||
if (ev.getPointerCount() > 2) {
|
||||
// Using more than two fingers causes weird behavior, so just cancel the pinch.
|
||||
cancelPinch(mPreviousProgress, -1);
|
||||
} else {
|
||||
mPinchDetector.onTouchEvent(ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScaleBegin(ScaleGestureDetector detector) {
|
||||
if (mLauncher.mState != Launcher.State.WORKSPACE || mLauncher.isOnCustomContent()) {
|
||||
// Don't listen for the pinch gesture if on all apps, widget picker, -1, etc.
|
||||
return false;
|
||||
}
|
||||
if (mAnimationManager != null && mAnimationManager.isAnimating()) {
|
||||
// Don't listen for the pinch gesture if we are already animating from a previous one.
|
||||
return false;
|
||||
}
|
||||
if (mWorkspace == null) {
|
||||
mWorkspace = mLauncher.mWorkspace;
|
||||
mThresholdManager = new PinchThresholdManager(mWorkspace);
|
||||
mAnimationManager = new PinchAnimationManager(mLauncher);
|
||||
}
|
||||
if (mWorkspace.isSwitchingState() || mWorkspace.mScrollInteractionBegan) {
|
||||
// Don't listen to pinches occurring while switching state, as it will cause a jump
|
||||
// once the state switching animation is complete.
|
||||
return false;
|
||||
}
|
||||
|
||||
mPreviousProgress = mWorkspace.isInOverviewMode() ? OVERVIEW_PROGRESS : WORKSPACE_PROGRESS;
|
||||
mPreviousTimeMillis = System.currentTimeMillis();
|
||||
mInterpolator = mWorkspace.isInOverviewMode() ? new LogDecelerateInterpolator(100, 0)
|
||||
: new LogAccelerateInterpolator(100, 0);
|
||||
mPinchStarted = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScaleEnd(ScaleGestureDetector detector) {
|
||||
super.onScaleEnd(detector);
|
||||
|
||||
float passedThreshold = mThresholdManager.getPassedThreshold();
|
||||
// If we are going towards overview, mPreviousProgress is how much further we need to
|
||||
// go, since it is going from 1 to 0. If we are going to workspace, we want
|
||||
// 1 - mPreviousProgress.
|
||||
float remainingProgress = mPreviousProgress;
|
||||
if (mWorkspace.isInOverviewMode() || passedThreshold < PinchThresholdManager.THRESHOLD_ONE) {
|
||||
remainingProgress = 1f - mPreviousProgress;
|
||||
}
|
||||
int duration = computeDuration(remainingProgress, mProgressDelta, mTimeDelta);
|
||||
|
||||
if (passedThreshold < PinchThresholdManager.THRESHOLD_ONE) {
|
||||
cancelPinch(mPreviousProgress, duration);
|
||||
} else if (passedThreshold < PinchThresholdManager.THRESHOLD_THREE) {
|
||||
float toProgress = mWorkspace.isInOverviewMode() ?
|
||||
WORKSPACE_PROGRESS : OVERVIEW_PROGRESS;
|
||||
mAnimationManager.animateToProgress(mPreviousProgress, toProgress, duration,
|
||||
mThresholdManager);
|
||||
} else {
|
||||
mThresholdManager.reset();
|
||||
}
|
||||
mPinchStarted = false;
|
||||
mPinchCanceled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the amount of time required to complete the transition based on the current pinch
|
||||
* speed. If this time is too long, instead return the normal duration, ignoring the speed.
|
||||
*/
|
||||
private int computeDuration(float remainingProgress, float progressDelta, long timeDelta) {
|
||||
float progressSpeed = Math.abs(progressDelta) / timeDelta;
|
||||
int remainingMillis = (int) (remainingProgress / progressSpeed);
|
||||
return Math.min(remainingMillis, mAnimationManager.getNormalOverviewTransitionDuration());
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the current pinch, returning back to where the pinch started (either workspace or
|
||||
* overview). If duration is -1, the default overview transition duration is used.
|
||||
*/
|
||||
private void cancelPinch(float currentProgress, int duration) {
|
||||
if (mPinchCanceled) return;
|
||||
mPinchCanceled = true;
|
||||
float toProgress = mWorkspace.isInOverviewMode() ? OVERVIEW_PROGRESS : WORKSPACE_PROGRESS;
|
||||
mAnimationManager.animateToProgress(currentProgress, toProgress, duration,
|
||||
mThresholdManager);
|
||||
mPinchStarted = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScale(ScaleGestureDetector detector) {
|
||||
if (mThresholdManager.getPassedThreshold() == PinchThresholdManager.THRESHOLD_THREE) {
|
||||
// We completed the pinch, so stop listening to further movement until user lets go.
|
||||
return true;
|
||||
}
|
||||
if (mLauncher.getDragController().isDragging()) {
|
||||
mLauncher.getDragController().cancelDrag();
|
||||
}
|
||||
|
||||
float pinchDist = detector.getCurrentSpan() - detector.getPreviousSpan();
|
||||
if (pinchDist < 0 && mWorkspace.isInOverviewMode() ||
|
||||
pinchDist > 0 && !mWorkspace.isInOverviewMode()) {
|
||||
// Pinching the wrong way, so ignore.
|
||||
return false;
|
||||
}
|
||||
// Pinch distance must equal the workspace width before switching states.
|
||||
int pinchDistanceToCompleteTransition = mWorkspace.getWidth();
|
||||
float overviewScale = mWorkspace.getOverviewModeShrinkFactor();
|
||||
float initialWorkspaceScale = mWorkspace.isInOverviewMode() ? overviewScale : 1f;
|
||||
float pinchScale = initialWorkspaceScale + pinchDist / pinchDistanceToCompleteTransition;
|
||||
// Bound the scale between the overview scale and the normal workspace scale (1f).
|
||||
pinchScale = Math.max(overviewScale, Math.min(pinchScale, 1f));
|
||||
// Progress ranges from 0 to 1, where 0 corresponds to the overview scale and 1
|
||||
// corresponds to the normal workspace scale (1f).
|
||||
float progress = (pinchScale - overviewScale) / (1f - overviewScale);
|
||||
float interpolatedProgress = mInterpolator.getInterpolation(progress);
|
||||
|
||||
mAnimationManager.setAnimationProgress(interpolatedProgress);
|
||||
float passedThreshold = mThresholdManager.updateAndAnimatePassedThreshold(
|
||||
interpolatedProgress, mAnimationManager);
|
||||
if (passedThreshold == PinchThresholdManager.THRESHOLD_THREE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
mProgressDelta = interpolatedProgress - mPreviousProgress;
|
||||
mPreviousProgress = interpolatedProgress;
|
||||
mTimeDelta = System.currentTimeMillis() - mPreviousTimeMillis;
|
||||
mPreviousTimeMillis = System.currentTimeMillis();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1900,6 +1900,10 @@ public class Workspace extends PagedView
|
||||
return -(workspaceHeight - scaledHeight) / 2;
|
||||
}
|
||||
|
||||
float getOverviewModeShrinkFactor() {
|
||||
return mOverviewModeShrinkFactor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current workspace {@link State}, returning an animation transitioning the workspace
|
||||
* to that new state.
|
||||
@@ -1996,6 +2000,10 @@ public class Workspace extends PagedView
|
||||
|
||||
void updateCustomContentVisibility() {
|
||||
int visibility = mState == Workspace.State.NORMAL ? VISIBLE : INVISIBLE;
|
||||
setCustomContentVisibility(visibility);
|
||||
}
|
||||
|
||||
void setCustomContentVisibility(int visibility) {
|
||||
if (hasCustomContent()) {
|
||||
mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID).setVisibility(visibility);
|
||||
}
|
||||
@@ -3526,6 +3534,10 @@ public class Workspace extends PagedView
|
||||
}
|
||||
}
|
||||
|
||||
public WorkspaceStateTransitionAnimation getStateTransitionAnimation() {
|
||||
return mStateTransitionAnimation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current {@link CellLayout}, correctly picking the destination
|
||||
* screen while a scroll is in progress.
|
||||
|
||||
@@ -47,6 +47,7 @@ import com.android.launcher3.ItemInfo;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.LauncherAppWidgetHostView;
|
||||
import com.android.launcher3.PinchToOverviewListener;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.SearchDropTargetBar;
|
||||
import com.android.launcher3.ShortcutAndWidgetContainer;
|
||||
@@ -110,6 +111,8 @@ public class DragLayer extends InsettableFrameLayout {
|
||||
private Drawable mLeftHoverDrawableActive;
|
||||
private Drawable mRightHoverDrawableActive;
|
||||
|
||||
// Related to pinch-to-go-to-overview gesture.
|
||||
private PinchToOverviewListener mPinchListener;
|
||||
/**
|
||||
* Used to create a new DragLayer from XML.
|
||||
*
|
||||
@@ -134,6 +137,8 @@ public class DragLayer extends InsettableFrameLayout {
|
||||
public void setup(Launcher launcher, DragController controller) {
|
||||
mLauncher = launcher;
|
||||
mDragController = controller;
|
||||
|
||||
mPinchListener = new PinchToOverviewListener(mLauncher);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -241,6 +246,14 @@ public class DragLayer extends InsettableFrameLayout {
|
||||
mTouchCompleteListener = null;
|
||||
}
|
||||
clearAllResizeFrames();
|
||||
|
||||
Folder currentFolder = mLauncher.getWorkspace().getOpenFolder();
|
||||
if (currentFolder == null) {
|
||||
if (mPinchListener.onInterceptTouchEvent(ev)) {
|
||||
// Stop listening for scrolling etc. (onTouchEvent() handles the rest of the pinch.)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return mDragController.onInterceptTouchEvent(ev);
|
||||
}
|
||||
|
||||
@@ -354,6 +367,10 @@ public class DragLayer extends InsettableFrameLayout {
|
||||
int x = (int) ev.getX();
|
||||
int y = (int) ev.getY();
|
||||
|
||||
// This is only reached if a pinch was started from onInterceptTouchEvent();
|
||||
// this continues sending events for it.
|
||||
mPinchListener.onTouchEvent(ev);
|
||||
|
||||
if (action == MotionEvent.ACTION_DOWN) {
|
||||
if (handleTouchDown(ev, false)) {
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user