blob: 1fbd0dc13ce3e6a07ffcda4fcad136ca962ce0d7 [file] [log] [blame]
package com.android.launcher3.util;
import android.app.WallpaperManager;
import android.os.IBinder;
import android.util.Log;
import android.view.Choreographer;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
/**
* Utility class to handle wallpaper scrolling along with workspace.
*/
public class WallpaperOffsetInterpolator implements Choreographer.FrameCallback {
private static final String TAG = "WPOffsetInterpolator";
private static final int ANIMATION_DURATION = 250;
// Don't use all the wallpaper for parallax until you have at least this many pages
private static final int MIN_PARALLAX_PAGE_SPAN = 3;
private final Choreographer mChoreographer;
private final Interpolator mInterpolator;
private final WallpaperManager mWallpaperManager;
private final Workspace mWorkspace;
private final boolean mIsRtl;
private IBinder mWindowToken;
private boolean mWallpaperIsLiveWallpaper;
private float mLastSetWallpaperOffsetSteps = 0;
private float mFinalOffset = 0.0f;
private float mCurrentOffset = 0.5f; // to force an initial update
private boolean mWaitingForUpdate;
private boolean mAnimating;
private long mAnimationStartTime;
private float mAnimationStartOffset;
int mNumScreens;
int mNumPagesForWallpaperParallax;
public WallpaperOffsetInterpolator(Workspace workspace) {
mChoreographer = Choreographer.getInstance();
mInterpolator = new DecelerateInterpolator(1.5f);
mWorkspace = workspace;
mWallpaperManager = WallpaperManager.getInstance(workspace.getContext());
mIsRtl = Utilities.isRtl(workspace.getResources());
}
@Override
public void doFrame(long frameTimeNanos) {
updateOffset(false);
}
private void updateOffset(boolean force) {
if (mWaitingForUpdate || force) {
mWaitingForUpdate = false;
if (computeScrollOffset() && mWindowToken != null) {
try {
mWallpaperManager.setWallpaperOffsets(mWindowToken, getCurrX(), 0.5f);
setWallpaperOffsetSteps();
} catch (IllegalArgumentException e) {
Log.e(TAG, "Error updating wallpaper offset: " + e);
}
}
}
}
public boolean computeScrollOffset() {
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 {
mCurrentOffset = mFinalOffset;
}
if (Math.abs(mCurrentOffset - mFinalOffset) > 0.0000001f) {
scheduleUpdate();
}
if (Math.abs(oldOffset - mCurrentOffset) > 0.0000001f) {
return true;
}
return false;
}
public float wallpaperOffsetForScroll(int scroll) {
// TODO: do different behavior if it's a live wallpaper?
// Don't use up all the wallpaper parallax until you have at least
// MIN_PARALLAX_PAGE_SPAN pages
int numScrollingPages = getNumScreensExcludingEmptyAndCustom();
int parallaxPageSpan;
if (mWallpaperIsLiveWallpaper) {
parallaxPageSpan = numScrollingPages - 1;
} else {
parallaxPageSpan = Math.max(MIN_PARALLAX_PAGE_SPAN, numScrollingPages - 1);
}
mNumPagesForWallpaperParallax = parallaxPageSpan;
if (mWorkspace.getChildCount() <= 1) {
if (mIsRtl) {
return 1 - 1.0f/mNumPagesForWallpaperParallax;
}
return 0;
}
// Exclude the leftmost page
int emptyExtraPages = numEmptyScreensToIgnore();
int firstIndex = mWorkspace.numCustomPages();
// Exclude the last extra empty screen (if we have > MIN_PARALLAX_PAGE_SPAN pages)
int lastIndex = mWorkspace.getChildCount() - 1 - emptyExtraPages;
if (mIsRtl) {
int temp = firstIndex;
firstIndex = lastIndex;
lastIndex = temp;
}
int firstPageScrollX = mWorkspace.getScrollForPage(firstIndex);
int scrollRange = mWorkspace.getScrollForPage(lastIndex) - firstPageScrollX;
if (scrollRange == 0) {
return 0;
} else {
// Sometimes the left parameter of the pages is animated during a layout transition;
// this parameter offsets it to keep the wallpaper from animating as well
int adjustedScroll =
scroll - firstPageScrollX - mWorkspace.getLayoutTransitionOffsetForPage(0);
float offset = Math.min(1, adjustedScroll / (float) scrollRange);
offset = Math.max(0, offset);
// On RTL devices, push the wallpaper offset to the right if we don't have enough
// pages (ie if numScrollingPages < MIN_PARALLAX_PAGE_SPAN)
if (!mWallpaperIsLiveWallpaper && numScrollingPages < MIN_PARALLAX_PAGE_SPAN
&& mIsRtl) {
return offset * (parallaxPageSpan - numScrollingPages + 1) / parallaxPageSpan;
}
return offset * (numScrollingPages - 1) / parallaxPageSpan;
}
}
private float wallpaperOffsetForCurrentScroll() {
return wallpaperOffsetForScroll(mWorkspace.getScrollX());
}
private int numEmptyScreensToIgnore() {
int numScrollingPages = mWorkspace.getChildCount() - mWorkspace.numCustomPages();
if (numScrollingPages >= MIN_PARALLAX_PAGE_SPAN && mWorkspace.hasExtraEmptyScreen()) {
return 1;
} else {
return 0;
}
}
private int getNumScreensExcludingEmptyAndCustom() {
return mWorkspace.getChildCount() - numEmptyScreensToIgnore() - mWorkspace.numCustomPages();
}
public void syncWithScroll() {
float offset = wallpaperOffsetForCurrentScroll();
setFinalX(offset);
updateOffset(true);
}
public float getCurrX() {
return mCurrentOffset;
}
public float getFinalX() {
return mFinalOffset;
}
private void animateToFinal() {
mAnimating = true;
mAnimationStartOffset = mCurrentOffset;
mAnimationStartTime = System.currentTimeMillis();
}
private void setWallpaperOffsetSteps() {
// Set wallpaper offset steps (1 / (number of screens - 1))
float xOffset = 1.0f / mNumPagesForWallpaperParallax;
if (xOffset != mLastSetWallpaperOffsetSteps) {
mWallpaperManager.setWallpaperOffsetSteps(xOffset, 1.0f);
mLastSetWallpaperOffsetSteps = xOffset;
}
}
public void setFinalX(float x) {
scheduleUpdate();
mFinalOffset = Math.max(0f, Math.min(x, 1.0f));
if (getNumScreensExcludingEmptyAndCustom() != mNumScreens) {
if (mNumScreens > 0) {
// Don't animate if we're going from 0 screens
animateToFinal();
}
mNumScreens = getNumScreensExcludingEmptyAndCustom();
}
}
private void scheduleUpdate() {
if (!mWaitingForUpdate) {
mChoreographer.postFrameCallback(this);
mWaitingForUpdate = true;
}
}
public void jumpToFinal() {
mCurrentOffset = mFinalOffset;
}
public void onResume() {
mWallpaperIsLiveWallpaper = mWallpaperManager.getWallpaperInfo() != null;
// Force the wallpaper offset steps to be set again, because another app might have changed
// them
mLastSetWallpaperOffsetSteps = 0f;
}
public void setWindowToken(IBinder token) {
mWindowToken = token;
}
}