Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8e35dbb2 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Moving the wallpaper offset interpolator to background thread" into ub-launcher3-master

parents 0474222c d0c3d3a5
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -917,7 +917,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(
+0 −4
Original line number Diff line number Diff line
@@ -1454,10 +1454,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) {
+164 −131
Original line number Diff line number Diff line
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());
    public float wallpaperOffsetForScroll(int scroll) {
        wallpaperOffsetForScroll(scroll, getNumScreensExcludingEmpty(), sTempInt);
        return ((float) sTempInt[0]) / sTempInt[1];
    }

    private int numEmptyScreensToIgnore() {
    private int getNumScreensExcludingEmpty() {
        int numScrollingPages = mWorkspace.getChildCount();
        if (numScrollingPages >= MIN_PARALLAX_PAGE_SPAN && mWorkspace.hasExtraEmptyScreen()) {
            return 1;
            return numScrollingPages - 1;
        } else {
            return 0;
            return numScrollingPages;
        }
    }

    private int getNumScreensExcludingEmpty() {
        return mWorkspace.getChildCount() - numEmptyScreensToIgnore();
    public void syncWithScroll() {
        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 = numScreens;
            updateOffset();
        }
        msg.sendToTarget();
    }

    public void syncWithScroll() {
        float offset = wallpaperOffsetForCurrentScroll();
        setFinalX(offset);
        updateOffset(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 float getCurrX() {
        return mCurrentOffset;
    public void jumpToFinal() {
        Message.obtain(mHandler, MSG_JUMP_TO_FINAL, mWindowToken).sendToTarget();
    }

    public float getFinalX() {
        return mFinalOffset;
    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;
        }
    }

    private void animateToFinal() {
        mAnimating = true;
        mAnimationStartOffset = mCurrentOffset;
        mAnimationStartTime = System.currentTimeMillis();
    @Override
    public void onReceive(Context context, Intent intent) {
        mWallpaperIsLiveWallpaper =
                WallpaperManager.getInstance(mWorkspace.getContext()).getWallpaperInfo() != null;
        updateOffset();
    }

    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;
    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;
            }

    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();
            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;
                    }
            mNumScreens = getNumScreensExcludingEmpty();

                    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();
                    }

    private void scheduleUpdate() {
        if (!mWaitingForUpdate) {
            mChoreographer.postFrameCallback(this);
            mWaitingForUpdate = true;
                    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;
                }

    public void jumpToFinal() {
                case MSG_JUMP_TO_FINAL: {
                    if (Float.compare(mCurrentOffset, mFinalOffset) != 0) {
                        mCurrentOffset = mFinalOffset;
                        setOffsetSafely(token);
                    }
                    mAnimating = false;
                    return;
                }
            }

    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;
        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