From d0c3d3a53c4df1669d53dc7111e71a86a340a2c2 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 3 Oct 2017 13:39:42 -0700 Subject: [PATCH] Moving the wallpaper offset interpolator to background thread Bug: 67305604 Bug: 25321240 Change-Id: I2d667a4d1a4b91ec2a11e5e171f8a1d1c4222b33 --- src/com/android/launcher3/Launcher.java | 1 - src/com/android/launcher3/Workspace.java | 4 - .../util/WallpaperOffsetInterpolator.java | 307 ++++++++++-------- 3 files changed, 170 insertions(+), 142 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 4388eb4a0a..6342bc0639 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -945,7 +945,6 @@ public class Launcher extends BaseActivity } updateInteraction(Workspace.State.NORMAL, mWorkspace.getState()); - mWorkspace.onResume(); // Process any items that were added while Launcher was away. InstallShortcutReceiver.disableAndFlushInstallQueue( diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 74c96a310c..53110dccac 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -1459,10 +1459,6 @@ public class Workspace extends PagedView mWallpaperOffset.setWindowToken(null); } - protected void onResume() { - mWallpaperOffset.onResume(); - } - @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { if (mUnlockWallpaperFromDefaultPageOnLayout) { diff --git a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java index f99efcec94..ec494f1818 100644 --- a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java +++ b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java @@ -1,9 +1,15 @@ package com.android.launcher3.util; import android.app.WallpaperManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Handler; import android.os.IBinder; +import android.os.Message; +import android.os.SystemClock; import android.util.Log; -import android.view.Choreographer; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; @@ -13,60 +19,30 @@ import com.android.launcher3.Workspace; /** * Utility class to handle wallpaper scrolling along with workspace. */ -public class WallpaperOffsetInterpolator implements Choreographer.FrameCallback { +public class WallpaperOffsetInterpolator extends BroadcastReceiver { + + private static final int[] sTempInt = new int[2]; 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 = 4; - private final Choreographer mChoreographer; - private final Interpolator mInterpolator; - private final WallpaperManager mWallpaperManager; private final Workspace mWorkspace; private final boolean mIsRtl; + private final Handler mHandler; + private boolean mRegistered = false; 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 mLockedToDefaultPage; - - private boolean mAnimating; - private long mAnimationStartTime; - private float mAnimationStartOffset; - int mNumScreens; - int mNumPagesForWallpaperParallax; + private int mNumScreens; 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); - } - } - } + mHandler = new OffsetHandler(workspace.getContext()); } /** @@ -80,46 +56,25 @@ public class WallpaperOffsetInterpolator implements Choreographer.FrameCallback return mLockedToDefaultPage; } - 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; - } - /** + * Computes the wallpaper offset as an int ratio (out[0] / out[1]) + * * TODO: do different behavior if it's a live wallpaper? */ - public float wallpaperOffsetForScroll(int scroll) { + private void wallpaperOffsetForScroll(int scroll, int numScrollingPages, final int[] out) { + out[1] = 1; + // To match the default wallpaper behavior in the system, we default to either the left // or right edge on initialization - int numScrollingPages = getNumScreensExcludingEmpty(); if (mLockedToDefaultPage || numScrollingPages <= 1) { - return mIsRtl ? 1f : 0f; + out[0] = mIsRtl ? 1 : 0; + return; } // Distribute the wallpaper parallax over a minimum of MIN_PARALLAX_PAGE_SPAN workspace // screens, not including the custom screen, and empty screens (if > MIN_PARALLAX_PAGE_SPAN) - if (mWallpaperIsLiveWallpaper) { - mNumPagesForWallpaperParallax = numScrollingPages; - } else { - mNumPagesForWallpaperParallax = Math.max(MIN_PARALLAX_PAGE_SPAN, numScrollingPages); - } + int numPagesForWallpaperParallax = mWallpaperIsLiveWallpaper ? numScrollingPages : + Math.max(MIN_PARALLAX_PAGE_SPAN, numScrollingPages); // Offset by the custom screen int leftPageIndex; @@ -136,106 +91,184 @@ public class WallpaperOffsetInterpolator implements Choreographer.FrameCallback int leftPageScrollX = mWorkspace.getScrollForPage(leftPageIndex); int rightPageScrollX = mWorkspace.getScrollForPage(rightPageIndex); int scrollRange = rightPageScrollX - leftPageScrollX; - if (scrollRange == 0) { - return 0f; + if (scrollRange <= 0) { + out[0] = 0; + return; } // 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 - leftPageScrollX - mWorkspace.getLayoutTransitionOffsetForPage(0); - float offset = Utilities.boundToRange((float) adjustedScroll / scrollRange, 0f, 1f); + adjustedScroll = Utilities.boundToRange(adjustedScroll, 0, scrollRange); + out[1] = (numPagesForWallpaperParallax - 1) * scrollRange; // The offset is now distributed 0..1 between the left and right pages that we care about, // so we just map that between the pages that we are using for parallax - float rtlOffset = 0; + int rtlOffset = 0; if (mIsRtl) { // In RTL, the pages are right aligned, so adjust the offset from the end - rtlOffset = (float) ((mNumPagesForWallpaperParallax - 1) - (numScrollingPages - 1)) / - (mNumPagesForWallpaperParallax - 1); + rtlOffset = out[1] - (numScrollingPages - 1) * scrollRange; } - return rtlOffset + offset * - ((float) (numScrollingPages - 1) / (mNumPagesForWallpaperParallax - 1)); + out[0] = rtlOffset + adjustedScroll * (numScrollingPages - 1); } - private float wallpaperOffsetForCurrentScroll() { - return wallpaperOffsetForScroll(mWorkspace.getScrollX()); - } - - private int numEmptyScreensToIgnore() { - int numScrollingPages = mWorkspace.getChildCount(); - if (numScrollingPages >= MIN_PARALLAX_PAGE_SPAN && mWorkspace.hasExtraEmptyScreen()) { - return 1; - } else { - return 0; - } + public float wallpaperOffsetForScroll(int scroll) { + wallpaperOffsetForScroll(scroll, getNumScreensExcludingEmpty(), sTempInt); + return ((float) sTempInt[0]) / sTempInt[1]; } private int getNumScreensExcludingEmpty() { - return mWorkspace.getChildCount() - numEmptyScreensToIgnore(); + int numScrollingPages = mWorkspace.getChildCount(); + if (numScrollingPages >= MIN_PARALLAX_PAGE_SPAN && mWorkspace.hasExtraEmptyScreen()) { + return numScrollingPages - 1; + } else { + return numScrollingPages; + } } 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 - 1); - if (xOffset != mLastSetWallpaperOffsetSteps) { - mWallpaperManager.setWallpaperOffsetSteps(xOffset, 1.0f); - mLastSetWallpaperOffsetSteps = xOffset; - } - } - - public void setFinalX(float x) { - scheduleUpdate(); - mFinalOffset = Math.max(0f, Math.min(x, 1f)); - if (getNumScreensExcludingEmpty() != mNumScreens) { - if (mNumScreens > 0 && Float.compare(mCurrentOffset, mFinalOffset) != 0) { - // Don't animate if we're going from 0 screens, or if the final offset is the same - // as the current offset - animateToFinal(); + int numScreens = getNumScreensExcludingEmpty(); + wallpaperOffsetForScroll(mWorkspace.getScrollX(), numScreens, sTempInt); + Message msg = Message.obtain(mHandler, MSG_UPDATE_OFFSET, sTempInt[0], sTempInt[1], + mWindowToken); + if (numScreens != mNumScreens) { + if (mNumScreens > 0) { + // Don't animate if we're going from 0 screens + msg.what = MSG_START_ANIMATION; } - mNumScreens = getNumScreensExcludingEmpty(); + mNumScreens = numScreens; + updateOffset(); } + msg.sendToTarget(); } - private void scheduleUpdate() { - if (!mWaitingForUpdate) { - mChoreographer.postFrameCallback(this); - mWaitingForUpdate = true; + private void updateOffset() { + int numPagesForWallpaperParallax; + if (mWallpaperIsLiveWallpaper) { + numPagesForWallpaperParallax = mNumScreens; + } else { + numPagesForWallpaperParallax = Math.max(MIN_PARALLAX_PAGE_SPAN, mNumScreens); } + Message.obtain(mHandler, MSG_SET_NUM_PARALLAX, numPagesForWallpaperParallax, 0, + mWindowToken).sendToTarget(); } 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; + Message.obtain(mHandler, MSG_JUMP_TO_FINAL, mWindowToken).sendToTarget(); } public void setWindowToken(IBinder token) { mWindowToken = token; + if (mWindowToken == null && mRegistered) { + mWorkspace.getContext().unregisterReceiver(this); + mRegistered = false; + } else if (mWindowToken != null && !mRegistered) { + mWorkspace.getContext() + .registerReceiver(this, new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED)); + onReceive(mWorkspace.getContext(), null); + mRegistered = true; + } + } + + @Override + public void onReceive(Context context, Intent intent) { + mWallpaperIsLiveWallpaper = + WallpaperManager.getInstance(mWorkspace.getContext()).getWallpaperInfo() != null; + updateOffset(); + } + + private static final int MSG_START_ANIMATION = 1; + private static final int MSG_UPDATE_OFFSET = 2; + private static final int MSG_APPLY_OFFSET = 3; + private static final int MSG_SET_NUM_PARALLAX = 4; + private static final int MSG_JUMP_TO_FINAL = 5; + + private static class OffsetHandler extends Handler { + + private final Interpolator mInterpolator; + private final WallpaperManager mWM; + + private float mCurrentOffset = 0.5f; // to force an initial update + private boolean mAnimating; + private long mAnimationStartTime; + private float mAnimationStartOffset; + + private float mFinalOffset; + private float mOffsetX; + + public OffsetHandler(Context context) { + super(UiThreadHelper.getBackgroundLooper()); + mInterpolator = new DecelerateInterpolator(1.5f); + mWM = WallpaperManager.getInstance(context); + } + + @Override + public void handleMessage(Message msg) { + final IBinder token = (IBinder) msg.obj; + if (token == null) { + return; + } + + switch (msg.what) { + case MSG_START_ANIMATION: { + mAnimating = true; + mAnimationStartOffset = mCurrentOffset; + mAnimationStartTime = msg.getWhen(); + // Follow through + } + case MSG_UPDATE_OFFSET: + mFinalOffset = ((float) msg.arg1) / msg.arg2; + // Follow through + case MSG_APPLY_OFFSET: { + float oldOffset = mCurrentOffset; + if (mAnimating) { + long durationSinceAnimation = SystemClock.uptimeMillis() + - 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 (Float.compare(mCurrentOffset, oldOffset) != 0) { + setOffsetSafely(token); + // Force the wallpaper offset steps to be set again, because another app + // might have changed them + mWM.setWallpaperOffsetSteps(mOffsetX, 1.0f); + } + if (mAnimating) { + // If we are animating, keep updating the offset + Message.obtain(this, MSG_APPLY_OFFSET, token).sendToTarget(); + } + return; + } + case MSG_SET_NUM_PARALLAX: { + // Set wallpaper offset steps (1 / (number of screens - 1)) + mOffsetX = 1.0f / (msg.arg1 - 1); + mWM.setWallpaperOffsetSteps(mOffsetX, 1.0f); + return; + } + case MSG_JUMP_TO_FINAL: { + if (Float.compare(mCurrentOffset, mFinalOffset) != 0) { + mCurrentOffset = mFinalOffset; + setOffsetSafely(token); + } + mAnimating = false; + return; + } + } + } + + private void setOffsetSafely(IBinder token) { + try { + mWM.setWallpaperOffsets(token, mCurrentOffset, 0.5f); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Error updating wallpaper offset: " + e); + } + } } } \ No newline at end of file