Add wallpaper parallax

- Currently only works with built-in wallpapers
- Animates smoothly when the number of wallpapers
changes

Change-Id: Ia63f5d4cdc6fc83495e507d93cf34405e6733515
This commit is contained in:
Michael Jurka 2013-08-26 20:56:35 +02:00
parent fd471c0b4f
commit cc07e7a9c6
1 changed files with 72 additions and 120 deletions

View File

@ -45,12 +45,13 @@ import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
import android.view.Choreographer;
import android.view.Display;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.widget.ImageView;
import android.view.animation.Interpolator;
import android.widget.TextView;
import com.android.launcher3.FolderIcon.FolderRingAnimator;
@ -189,7 +190,6 @@ public class Workspace extends SmoothPagedView
private boolean mWorkspaceFadeInAdjacentScreens;
WallpaperOffsetInterpolator mWallpaperOffset;
boolean mUpdateWallpaperOffsetImmediately = false;
private Runnable mDelayedResizeRunnable;
private Runnable mDelayedSnapToPageRunnable;
private Point mDisplaySize = new Point();
@ -940,38 +940,16 @@ public class Workspace extends SmoothPagedView
mLauncher.getSharedPrefs(), mLauncher.getWindowManager(), mWallpaperManager);
}
private void syncWallpaperOffsetWithScroll() {
final boolean enableWallpaperEffects = isHardwareAccelerated();
if (enableWallpaperEffects) {
// TODO: figure out what to do about parallax, for now disable it
//mWallpaperOffset.setFinalX(wallpaperOffsetForCurrentScroll());
}
}
public void updateWallpaperOffsetImmediately() {
mUpdateWallpaperOffsetImmediately = true;
}
private float wallpaperOffsetForCurrentScroll() {
// Set wallpaper offset steps (1 / (number of screens - 1))
mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 1.0f);
if (mMaxScrollX == 0) {
return 0;
}
private void updateWallpaperOffsets() {
boolean updateNow = false;
boolean keepUpdating = true;
if (mUpdateWallpaperOffsetImmediately) {
updateNow = true;
keepUpdating = false;
mWallpaperOffset.jumpToFinal();
mUpdateWallpaperOffsetImmediately = false;
} else {
updateNow = keepUpdating = mWallpaperOffset.computeScrollOffset();
}
if (updateNow) {
if (mWindowToken != null) {
mWallpaperManager.setWallpaperOffsets(mWindowToken,
mWallpaperOffset.getCurrX(), mWallpaperOffset.getCurrY());
}
}
if (keepUpdating) {
invalidate();
}
// do different behavior if it's alive wallpaper?
return getScrollX() / (float) mMaxScrollX;
}
protected void snapToPage(int whichPage, Runnable r) {
@ -986,114 +964,88 @@ public class Workspace extends SmoothPagedView
snapToPage(getPageIndexForScreenId(screenId), r);
}
class WallpaperOffsetInterpolator {
float mFinalHorizontalWallpaperOffset = 0.0f;
float mFinalVerticalWallpaperOffset = 0.5f;
float mHorizontalWallpaperOffset = 0.0f;
float mVerticalWallpaperOffset = 0.5f;
long mLastWallpaperOffsetUpdateTime;
boolean mIsMovingFast;
boolean mOverrideHorizontalCatchupConstant;
float mHorizontalCatchupConstant = 0.35f;
float mVerticalCatchupConstant = 0.35f;
class WallpaperOffsetInterpolator implements Choreographer.FrameCallback {
float mFinalOffset = 0.0f;
float mCurrentOffset = 0.0f;
//long mLastWallpaperOffsetUpdateTime;
boolean mWaitingForCallback;
Choreographer mChoreographer;
Interpolator mInterpolator;
boolean mAnimating;
long mAnimationStartTime;
float mAnimationStartOffset;
final int ANIMATION_DURATION = 250;
int mNumScreens;
public WallpaperOffsetInterpolator() {
mChoreographer = Choreographer.getInstance();
mInterpolator = new DecelerateInterpolator(1.5f);
}
public void setOverrideHorizontalCatchupConstant(boolean override) {
mOverrideHorizontalCatchupConstant = override;
}
public void setHorizontalCatchupConstant(float f) {
mHorizontalCatchupConstant = f;
}
public void setVerticalCatchupConstant(float f) {
mVerticalCatchupConstant = f;
@Override
public void doFrame(long frameTimeNanos) {
mWaitingForCallback = false;
if (computeScrollOffset()) {
mWallpaperManager.setWallpaperOffsets(mWindowToken,
mWallpaperOffset.getCurrX(), 0f);
}
}
public boolean computeScrollOffset() {
if (Float.compare(mHorizontalWallpaperOffset, mFinalHorizontalWallpaperOffset) == 0 &&
Float.compare(mVerticalWallpaperOffset, mFinalVerticalWallpaperOffset) == 0) {
mIsMovingFast = false;
return false;
}
boolean isLandscape = mDisplaySize.x > mDisplaySize.y;
long currentTime = System.currentTimeMillis();
long timeSinceLastUpdate = currentTime - mLastWallpaperOffsetUpdateTime;
timeSinceLastUpdate = Math.min((long) (1000/30f), timeSinceLastUpdate);
timeSinceLastUpdate = Math.max(1L, timeSinceLastUpdate);
float xdiff = Math.abs(mFinalHorizontalWallpaperOffset - mHorizontalWallpaperOffset);
if (!mIsMovingFast && xdiff > 0.07) {
mIsMovingFast = true;
}
float fractionToCatchUpIn1MsHorizontal;
if (mOverrideHorizontalCatchupConstant) {
fractionToCatchUpIn1MsHorizontal = mHorizontalCatchupConstant;
} else if (mIsMovingFast) {
fractionToCatchUpIn1MsHorizontal = isLandscape ? 0.5f : 0.75f;
final float oldOffset = mCurrentOffset;
if (mAnimating) {
long durationSinceAnimation = System.currentTimeMillis() - mAnimationStartTime;
float t0 = durationSinceAnimation / (float) ANIMATION_DURATION;
float t1 = mInterpolator.getInterpolation(t0);
mCurrentOffset = mAnimationStartOffset +
(mFinalOffset - mAnimationStartOffset) * t1;
mAnimating = durationSinceAnimation < ANIMATION_DURATION;
} else {
// slow
fractionToCatchUpIn1MsHorizontal = isLandscape ? 0.27f : 0.5f;
}
float fractionToCatchUpIn1MsVertical = mVerticalCatchupConstant;
fractionToCatchUpIn1MsHorizontal /= 33f;
fractionToCatchUpIn1MsVertical /= 33f;
final float UPDATE_THRESHOLD = 0.00001f;
float hOffsetDelta = mFinalHorizontalWallpaperOffset - mHorizontalWallpaperOffset;
float vOffsetDelta = mFinalVerticalWallpaperOffset - mVerticalWallpaperOffset;
boolean jumpToFinalValue = Math.abs(hOffsetDelta) < UPDATE_THRESHOLD &&
Math.abs(vOffsetDelta) < UPDATE_THRESHOLD;
// Don't have any lag between workspace and wallpaper on non-large devices
if (!LauncherAppState.getInstance().isScreenLarge() || jumpToFinalValue) {
mHorizontalWallpaperOffset = mFinalHorizontalWallpaperOffset;
mVerticalWallpaperOffset = mFinalVerticalWallpaperOffset;
} else {
float percentToCatchUpVertical =
Math.min(1.0f, timeSinceLastUpdate * fractionToCatchUpIn1MsVertical);
float percentToCatchUpHorizontal =
Math.min(1.0f, timeSinceLastUpdate * fractionToCatchUpIn1MsHorizontal);
mHorizontalWallpaperOffset += percentToCatchUpHorizontal * hOffsetDelta;
mVerticalWallpaperOffset += percentToCatchUpVertical * vOffsetDelta;
mCurrentOffset = mFinalOffset;
}
mLastWallpaperOffsetUpdateTime = System.currentTimeMillis();
return true;
if (Math.abs(oldOffset - mCurrentOffset) > 0.0000001f) {
scheduleCallback();
return true;
}
return false;
}
public float getCurrX() {
return mHorizontalWallpaperOffset;
return mCurrentOffset;
}
public float getFinalX() {
return mFinalHorizontalWallpaperOffset;
return mFinalOffset;
}
public float getCurrY() {
return mVerticalWallpaperOffset;
}
public float getFinalY() {
return mFinalVerticalWallpaperOffset;
private void animateToFinal() {
mAnimating = true;
mAnimationStartOffset = mCurrentOffset;
mAnimationStartTime = System.currentTimeMillis();
}
public void setFinalX(float x) {
mFinalHorizontalWallpaperOffset = Math.max(0f, Math.min(x, 1.0f));
scheduleCallback();
mFinalOffset = Math.max(0f, Math.min(x, 1.0f));
if (getChildCount() != mNumScreens) {
if (mNumScreens > 0) {
// Don't animate if we're going from 0 screens
animateToFinal();
}
mNumScreens = getChildCount();
}
}
public void setFinalY(float y) {
mFinalVerticalWallpaperOffset = Math.max(0f, Math.min(y, 1.0f));
private void scheduleCallback() {
if (!mWaitingForCallback) {
mChoreographer.postFrameCallback(this);
mWaitingForCallback = true;
}
}
public void jumpToFinal() {
mHorizontalWallpaperOffset = mFinalHorizontalWallpaperOffset;
mVerticalWallpaperOffset = mFinalVerticalWallpaperOffset;
mCurrentOffset = mFinalOffset;
}
}
@ -1103,6 +1055,10 @@ public class Workspace extends SmoothPagedView
syncWallpaperOffsetWithScroll();
}
private void syncWallpaperOffsetWithScroll() {
mWallpaperOffset.setFinalX(wallpaperOffsetForCurrentScroll());
}
void showOutlines() {
if (!isSmall() && !mIsSwitchingState) {
if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();
@ -1339,15 +1295,14 @@ public class Workspace extends SmoothPagedView
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
mUpdateWallpaperOffsetImmediately = true;
syncWallpaperOffsetWithScroll();
mWallpaperOffset.jumpToFinal();
}
super.onLayout(changed, left, top, right, bottom);
}
@Override
protected void onDraw(Canvas canvas) {
updateWallpaperOffsets();
// Draw the background gradient if necessary
if (mBackground != null && mBackgroundAlpha > 0.0f && mDrawBackground) {
int alpha = (int) (mBackgroundAlpha * 255);
@ -1899,7 +1854,6 @@ public class Workspace extends SmoothPagedView
@Override
public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
mIsSwitchingState = false;
mWallpaperOffset.setOverrideHorizontalCatchupConstant(false);
updateChildrenLayersEnabled(false);
// The code in getChangeStateAnimation to determine initialAlpha and finalAlpha will ensure
// ensure that only the current page is visible during (and subsequently, after) the
@ -2057,8 +2011,6 @@ public class Workspace extends SmoothPagedView
}
public void beginDragShared(View child, DragSource source) {
Resources r = getResources();
// The drag bitmap follows the touch point around on the screen
final Bitmap b = createDragBitmap(child, new Canvas(), DRAG_BITMAP_PADDING);