Merge "Make All Apps<-->Workspace transition smoother"

This commit is contained in:
Michael Jurka
2011-12-12 19:49:11 -08:00
committed by Android (Google) Code Review
5 changed files with 142 additions and 125 deletions
@@ -54,7 +54,6 @@ public class AppsCustomizeTabHost extends TabHost implements LauncherTransitiona
private boolean mInTransition;
private boolean mResetAfterTransition;
private Animator mLauncherTransition;
public AppsCustomizeTabHost(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -341,29 +340,17 @@ public class AppsCustomizeTabHost extends TabHost implements LauncherTransitiona
}
}
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (mLauncherTransition != null) {
enableAndBuildHardwareLayer();
mLauncherTransition.start();
mLauncherTransition = null;
}
@Override
public View getContent() {
return mContent;
}
/* LauncherTransitionable overrides */
@Override
public boolean onLauncherTransitionStart(Launcher l, Animator animation, boolean toWorkspace) {
public void onLauncherTransitionStart(Launcher l, Animator animation, boolean toWorkspace) {
mInTransition = true;
boolean delayLauncherTransitionUntilLayout = false;
boolean animated = (animation != null);
mLauncherTransition = null;
// if the content wasn't visible before, delay the launcher animation until after a call
// to layout -- this prevents a blip
if (animated && mContent.getVisibility() == GONE) {
mLauncherTransition = animation;
delayLauncherTransitionUntilLayout = true;
}
mContent.setVisibility(VISIBLE);
if (!toWorkspace) {
@@ -371,7 +358,7 @@ public class AppsCustomizeTabHost extends TabHost implements LauncherTransitiona
// transition to prevent slowing down the animation)
mAppsCustomizePane.loadAssociatedPages(mAppsCustomizePane.getCurrentPage(), true);
}
if (animated && !delayLauncherTransitionUntilLayout) {
if (animated) {
enableAndBuildHardwareLayer();
}
@@ -382,7 +369,6 @@ public class AppsCustomizeTabHost extends TabHost implements LauncherTransitiona
mAppsCustomizePane.reset();
mResetAfterTransition = false;
}
return delayLauncherTransitionUntilLayout;
}
@Override
@@ -406,7 +392,7 @@ public class AppsCustomizeTabHost extends TabHost implements LauncherTransitiona
}
}
public void onResume() {
public void onWindowVisible() {
if (getVisibility() == VISIBLE) {
mContent.setVisibility(VISIBLE);
// We unload the widget previews when the UI is hidden, so need to reload pages
+90 -49
View File
@@ -28,7 +28,6 @@ import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.SearchManager;
import android.app.StatusBarManager;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
@@ -54,7 +53,6 @@ import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
@@ -80,6 +78,7 @@ import android.view.View;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.AccelerateDecelerateInterpolator;
@@ -568,28 +567,6 @@ public final class Launcher extends Activity
// When we resume Launcher, a different Activity might be responsible for the app
// market intent, so refresh the icon
updateAppMarketIcon();
mAppsCustomizeTabHost.onResume();
if (!mWorkspaceLoading) {
final ViewTreeObserver observer = mWorkspace.getViewTreeObserver();
final Workspace workspace = mWorkspace;
// We want to let Launcher draw itself at least once before we force it to build
// layers on all the workspace pages, so that transitioning to Launcher from other
// apps is nice and speedy. Usually the first call to preDraw doesn't correspond to
// a true draw so we wait until the second preDraw call to be safe
observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
boolean mFirstTime = true;
public boolean onPreDraw() {
if (mFirstTime) {
mFirstTime = false;
} else {
workspace.post(mBuildLayersRunnable);
observer.removeOnPreDrawListener(this);
}
return true;
}
});
}
clearTypedText();
}
@Override
@@ -1067,6 +1044,33 @@ public final class Launcher extends Activity
public void onWindowVisibilityChanged(int visibility) {
mVisible = visibility == View.VISIBLE;
updateRunning();
// The following code used to be in onResume, but it turns out onResume is called when
// you're in All Apps and click home to go to the workspace. onWindowVisibilityChanged
// is a more appropriate event to handle
if (mVisible) {
mAppsCustomizeTabHost.onWindowVisible();
if (!mWorkspaceLoading) {
final ViewTreeObserver observer = mWorkspace.getViewTreeObserver();
final Workspace workspace = mWorkspace;
// We want to let Launcher draw itself at least once before we force it to build
// layers on all the workspace pages, so that transitioning to Launcher from other
// apps is nice and speedy. Usually the first call to preDraw doesn't correspond to
// a true draw so we wait until the second preDraw call to be safe
observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
boolean mFirstTime = true;
public boolean onPreDraw() {
if (mFirstTime) {
mFirstTime = false;
} else {
//workspace.post(mBuildLayersRunnable);
observer.removeOnPreDrawListener(this);
}
return true;
}
});
}
clearTypedText();
}
}
private void sendAdvanceMessage(long delay) {
@@ -2218,7 +2222,8 @@ public final class Launcher extends Activity
setPivotsForZoom(toView, scale);
// Shrink workspaces away if going to AppsCustomize from workspace
mWorkspace.changeState(Workspace.State.SMALL, animated);
Animator workspaceAnim =
mWorkspace.getChangeStateAnimation(Workspace.State.SMALL, animated);
if (animated) {
final ValueAnimator scaleAnim = ValueAnimator.ofFloat(0f, 1f).setDuration(duration);
@@ -2236,14 +2241,17 @@ public final class Launcher extends Activity
alphaAnim.setInterpolator(new DecelerateInterpolator(1.5f));
alphaAnim.addUpdateListener(new LauncherAnimatorUpdateListener() {
public void onAnimationUpdate(float a, float b) {
// don't need to invalidate because we do so above
toView.setAlpha(a * 0f + b * 1f);
}
});
alphaAnim.setStartDelay(startDelay);
alphaAnim.start();
scaleAnim.addListener(new AnimatorListenerAdapter() {
// toView should appear right at the end of the workspace shrink
// animation
mStateAnimation = new AnimatorSet();
mStateAnimation.play(alphaAnim).after(startDelay);
mStateAnimation.play(scaleAnim).after(startDelay);
mStateAnimation.addListener(new AnimatorListenerAdapter() {
boolean animationCancelled = false;
@Override
@@ -2283,19 +2291,43 @@ public final class Launcher extends Activity
}
});
// toView should appear right at the end of the workspace shrink animation
mStateAnimation = new AnimatorSet();
mStateAnimation.play(scaleAnim).after(startDelay);
if (workspaceAnim != null) {
mStateAnimation.play(workspaceAnim);
}
boolean delayAnim = false;
if (toView instanceof LauncherTransitionable) {
LauncherTransitionable lt = (LauncherTransitionable) toView;
delayAnim = lt.onLauncherTransitionStart(instance, mStateAnimation, false);
LauncherTransitionable lt = (LauncherTransitionable) toView;
final ViewTreeObserver observer;
lt.onLauncherTransitionStart(instance, mStateAnimation, false);
// If any of the objects being animated haven't been measured/laid out
// yet, delay the animation until we get a layout pass
if ((lt.getContent().getMeasuredWidth() == 0) ||
(mWorkspace.getMeasuredWidth() == 0) ||
(toView.getMeasuredWidth() == 0)) {
observer = mWorkspace.getViewTreeObserver();
delayAnim = true;
} else {
observer = null;
}
// if the anim is delayed, the LauncherTransitionable is responsible for starting it
if (!delayAnim) {
// TODO: q-- what if this anim is cancelled before being started? or started after
// being cancelled?
if (delayAnim) {
final OnGlobalLayoutListener delayedStart = new OnGlobalLayoutListener() {
public void onGlobalLayout() {
mWorkspace.post(new Runnable() {
public void run() {
// Need to update pivots for zoom if layout changed
setPivotsForZoom(toView, scale);
mStateAnimation.start();
}
});
observer.removeGlobalOnLayoutListener(this);
}
};
observer.addOnGlobalLayoutListener(delayedStart);
} else {
setPivotsForZoom(toView, scale);
mStateAnimation.start();
}
} else {
@@ -2324,7 +2356,8 @@ public final class Launcher extends Activity
* This is the opposite of showAppsCustomizeHelper.
* @param animated If true, the transition will be animated.
*/
private void hideAppsCustomizeHelper(boolean animated, final boolean springLoaded) {
private void hideAppsCustomizeHelper(
State toState, boolean animated, final boolean springLoaded) {
if (mStateAnimation != null) {
mStateAnimation.cancel();
mStateAnimation = null;
@@ -2336,6 +2369,16 @@ public final class Launcher extends Activity
final float scaleFactor = (float)
res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor);
final View fromView = mAppsCustomizeTabHost;
Animator workspaceAnim = null;
if (toState == State.WORKSPACE) {
int stagger = res.getInteger(R.integer.config_appsCustomizeWorkspaceAnimationStagger);
workspaceAnim = mWorkspace.getChangeStateAnimation(
Workspace.State.NORMAL, animated, stagger);
} else if (toState == State.APPS_CUSTOMIZE_SPRING_LOADED) {
workspaceAnim = mWorkspace.getChangeStateAnimation(
Workspace.State.SPRING_LOADED, animated);
}
setPivotsForZoom(fromView, scaleFactor);
updateWallpaperVisibility(true);
@@ -2379,6 +2422,9 @@ public final class Launcher extends Activity
mStateAnimation = new AnimatorSet();
mStateAnimation.playTogether(scaleAnim, alphaAnim);
if (workspaceAnim != null) {
mStateAnimation.play(workspaceAnim);
}
mStateAnimation.start();
} else {
fromView.setVisibility(View.GONE);
@@ -2399,13 +2445,9 @@ public final class Launcher extends Activity
}
void showWorkspace(boolean animated) {
Resources res = getResources();
int stagger = res.getInteger(R.integer.config_appsCustomizeWorkspaceAnimationStagger);
mWorkspace.changeState(Workspace.State.NORMAL, animated, stagger);
if (mState != State.WORKSPACE) {
mWorkspace.setVisibility(View.VISIBLE);
hideAppsCustomizeHelper(animated, false);
hideAppsCustomizeHelper(State.WORKSPACE, animated, false);
// Show the search bar and hotseat
mSearchDropTargetBar.showSearchBar(animated);
@@ -2454,8 +2496,7 @@ public final class Launcher extends Activity
void enterSpringLoadedDragMode() {
if (mState == State.APPS_CUSTOMIZE) {
mWorkspace.changeState(Workspace.State.SPRING_LOADED);
hideAppsCustomizeHelper(true, true);
hideAppsCustomizeHelper(State.APPS_CUSTOMIZE_SPRING_LOADED, true, true);
hideDockDivider();
mState = State.APPS_CUSTOMIZE_SPRING_LOADED;
}
@@ -3327,7 +3368,7 @@ public final class Launcher extends Activity
}
interface LauncherTransitionable {
// return true if the callee will take care of start the animation by itself
boolean onLauncherTransitionStart(Launcher l, Animator animation, boolean toWorkspace);
View getContent();
void onLauncherTransitionStart(Launcher l, Animator animation, boolean toWorkspace);
void onLauncherTransitionEnd(Launcher l, Animator animation, boolean toWorkspace);
}
+1 -4
View File
@@ -1603,9 +1603,7 @@ public abstract class PagedView extends ViewGroup {
// First, clear any pages that should no longer be loaded
for (int i = 0; i < count; ++i) {
Page layout = (Page) getPageAt(i);
if ((immediateAndOnly && i != page) ||
(i < lowerPageBound) ||
(i > upperPageBound)) {
if ((i < lowerPageBound) || (i > upperPageBound)) {
if (layout.getPageChildCount() > 0) {
layout.removeAllViewsOnPage();
}
@@ -1617,7 +1615,6 @@ public abstract class PagedView extends ViewGroup {
if ((i != page) && immediateAndOnly) {
continue;
}
Page layout = (Page) getPageAt(i);
if (lowerPageBound <= i && i <= upperPageBound) {
if (mDirtyPageContent.get(i)) {
syncPageItems(i, (i == page) && immediateAndOnly);
@@ -109,6 +109,7 @@ public class PagedViewGridLayout extends GridLayout implements Page {
@Override
public void removeAllViewsOnPage() {
removeAllViews();
mOnLayoutListener = null;
destroyHardwareLayer();
}
+44 -52
View File
@@ -159,10 +159,7 @@ public class Workspace extends SmoothPagedView
enum State { NORMAL, SPRING_LOADED, SMALL };
private State mState = State.NORMAL;
private boolean mIsSwitchingState = false;
private boolean mSwitchStateAfterFirstLayout = false;
private State mStateAfterFirstLayout;
private AnimatorSet mAnimator;
private AnimatorListener mChangeStateAnimationListener;
boolean mAnimatingViewIntoPlace = false;
@@ -405,7 +402,6 @@ public class Workspace extends SmoothPagedView
public void onAnimationEnd(Animator animation) {
mIsSwitchingState = false;
mWallpaperOffset.setOverrideHorizontalCatchupConstant(false);
mAnimator = null;
updateChildrenLayersEnabled();
}
};
@@ -1249,19 +1245,6 @@ public class Workspace extends SmoothPagedView
mUpdateWallpaperOffsetImmediately = true;
}
super.onLayout(changed, left, top, right, bottom);
// if shrinkToBottom() is called on initialization, it has to be deferred
// until after the first call to onLayout so that it has the correct width
if (mSwitchStateAfterFirstLayout) {
mSwitchStateAfterFirstLayout = false;
// shrink can trigger a synchronous onLayout call, so we
// post this to avoid a stack overflow / tangled onLayout calls
post(new Runnable() {
public void run() {
changeState(mStateAfterFirstLayout, false);
}
});
}
}
@Override
@@ -1565,32 +1548,19 @@ public class Workspace extends SmoothPagedView
mNewRotationYs = new float[childCount];
}
public void changeState(State shrinkState) {
changeState(shrinkState, true);
Animator getChangeStateAnimation(final State state, boolean animated) {
return getChangeStateAnimation(state, animated, 0);
}
void changeState(final State state, boolean animated) {
changeState(state, animated, 0);
}
void changeState(final State state, boolean animated, int delay) {
Animator getChangeStateAnimation(final State state, boolean animated, int delay) {
if (mState == state) {
return;
}
if (mFirstLayout) {
// (mFirstLayout == "first layout has not happened yet")
// cancel any pending shrinks that were set earlier
mSwitchStateAfterFirstLayout = false;
mStateAfterFirstLayout = state;
return;
return null;
}
// Initialize animation arrays for the first time if necessary
initAnimationArrays();
// Cancel any running transition animations
if (mAnimator != null) mAnimator.cancel();
mAnimator = new AnimatorSet();
AnimatorSet anim = animated ? new AnimatorSet() : null;
// Stop any scrolling, move to the current page right away
setCurrentPage((mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage);
@@ -1717,6 +1687,21 @@ public class Workspace extends SmoothPagedView
}
}
});
for (int i = 0; i < getChildCount(); i++) {
invalidate();
if (mOldAlphas[i] == 0 && mNewAlphas[i] == 0) {
final CellLayout cl = (CellLayout) getChildAt(i);
cl.fastInvalidate();
cl.setFastTranslationX(mNewTranslationXs[i]);
cl.setFastTranslationY(mNewTranslationYs[i]);
cl.setFastScaleX(mNewScaleXs[i]);
cl.setFastScaleY(mNewScaleYs[i]);
cl.setFastBackgroundAlpha(mNewBackgroundAlphas[i]);
cl.setBackgroundAlphaMultiplier(mNewBackgroundAlphaMultipliers[i]);
cl.setFastAlpha(mNewAlphas[i]);
}
}
animWithInterpolator.addUpdateListener(new LauncherAnimatorUpdateListener() {
public void onAnimationUpdate(float a, float b) {
mTransitionProgress = b;
@@ -1726,17 +1711,21 @@ public class Workspace extends SmoothPagedView
}
invalidate();
for (int i = 0; i < getChildCount(); i++) {
final CellLayout cl = (CellLayout) getChildAt(i);
cl.fastInvalidate();
cl.setFastTranslationX(a * mOldTranslationXs[i] + b * mNewTranslationXs[i]);
cl.setFastTranslationY(a * mOldTranslationYs[i] + b * mNewTranslationYs[i]);
cl.setFastScaleX(a * mOldScaleXs[i] + b * mNewScaleXs[i]);
cl.setFastScaleY(a * mOldScaleYs[i] + b * mNewScaleYs[i]);
cl.setFastBackgroundAlpha(
a * mOldBackgroundAlphas[i] + b * mNewBackgroundAlphas[i]);
cl.setBackgroundAlphaMultiplier(a * mOldBackgroundAlphaMultipliers[i] +
b * mNewBackgroundAlphaMultipliers[i]);
cl.setFastAlpha(a * mOldAlphas[i] + b * mNewAlphas[i]);
if (mOldAlphas[i] != 0 || mNewAlphas[i] != 0) {
final CellLayout cl = (CellLayout) getChildAt(i);
cl.fastInvalidate();
cl.setFastTranslationX(
a * mOldTranslationXs[i] + b * mNewTranslationXs[i]);
cl.setFastTranslationY(
a * mOldTranslationYs[i] + b * mNewTranslationYs[i]);
cl.setFastScaleX(a * mOldScaleXs[i] + b * mNewScaleXs[i]);
cl.setFastScaleY(a * mOldScaleYs[i] + b * mNewScaleYs[i]);
cl.setFastBackgroundAlpha(
a * mOldBackgroundAlphas[i] + b * mNewBackgroundAlphas[i]);
cl.setBackgroundAlphaMultiplier(a * mOldBackgroundAlphaMultipliers[i] +
b * mNewBackgroundAlphaMultipliers[i]);
cl.setFastAlpha(a * mOldAlphas[i] + b * mNewAlphas[i]);
}
}
syncChildrenLayersEnabledOnVisiblePages();
}
@@ -1752,18 +1741,20 @@ public class Workspace extends SmoothPagedView
return;
}
for (int i = 0; i < getChildCount(); i++) {
final CellLayout cl = (CellLayout) getChildAt(i);
cl.setFastRotationY(a * mOldRotationYs[i] + b * mNewRotationYs[i]);
if (mOldAlphas[i] != 0 || mNewAlphas[i] != 0 ||
mOldRotationYs[i] != 0 || mNewRotationYs[i] != 0) {
final CellLayout cl = (CellLayout) getChildAt(i);
cl.setFastRotationY(a * mOldRotationYs[i] + b * mNewRotationYs[i]);
}
}
}
});
mAnimator.playTogether(animWithInterpolator, rotationAnim);
mAnimator.setStartDelay(delay);
anim.playTogether(animWithInterpolator, rotationAnim);
anim.setStartDelay(delay);
// If we call this when we're not animated, onAnimationEnd is never called on
// the listener; make sure we only use the listener when we're actually animating
mAnimator.addListener(mChangeStateAnimationListener);
mAnimator.start();
anim.addListener(mChangeStateAnimationListener);
}
if (stateIsSpringLoaded) {
@@ -1777,6 +1768,7 @@ public class Workspace extends SmoothPagedView
animateBackgroundGradient(0f, true);
}
syncChildrenLayersEnabledOnVisiblePages();
return anim;
}
/**