Refactoring search bar animations.

- This refactoring ensures that LauncherStateTransition does not
  do its own animation on the QSB, and that all animations to the
  SearchDropTargetBar go through its own animators.

Bug: 22515084

Change-Id: Ia7d13c44d861eac7517076b52a9651a90911ed0a
This commit is contained in:
Winson Chung 2015-08-03 14:40:11 -07:00 committed by Winson
parent 153b02a5e6
commit 006ee269ba
9 changed files with 291 additions and 311 deletions

View File

@ -25,7 +25,6 @@ import android.graphics.Paint.FontMetrics;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.util.Size;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
@ -385,7 +384,7 @@ public class DeviceProfile {
final boolean isLayoutRtl = Utilities.isRtl(launcher.getResources());
// Layout the search bar space
View searchBar = launcher.getSearchBar();
View searchBar = launcher.getSearchDropTargetBar();
lp = (FrameLayout.LayoutParams) searchBar.getLayoutParams();
if (hasVerticalBarLayout) {
// Vertical search bar space -- The search bar is fixed in the layout to be on the left

View File

@ -159,7 +159,7 @@ public class DragLayer extends InsettableFrameLayout {
}
private boolean isEventOverDropTargetBar(MotionEvent ev) {
getDescendantRectRelativeToSelf(mLauncher.getSearchBar(), mHitRect);
getDescendantRectRelativeToSelf(mLauncher.getSearchDropTargetBar(), mHitRect);
if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) {
return true;
}
@ -321,7 +321,7 @@ public class DragLayer extends InsettableFrameLayout {
childrenForAccessibility.add(currentFolder);
if (isInAccessibleDrag()) {
childrenForAccessibility.add(mLauncher.getSearchBar());
childrenForAccessibility.add(mLauncher.getSearchDropTargetBar());
}
} else {
super.addChildrenForAccessibility(childrenForAccessibility);

View File

@ -275,7 +275,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
@Override
public void enableAccessibleDrag(boolean enable) {
mLauncher.getSearchBar().enableAccessibleDrag(enable);
mLauncher.getSearchDropTargetBar().enableAccessibleDrag(enable);
for (int i = 0; i < mContent.getChildCount(); i++) {
mContent.getPageAt(i).enableAccessibleDrag(enable, CellLayout.FOLDER_ACCESSIBILITY_DRAG);
}

View File

@ -132,8 +132,7 @@ import java.util.concurrent.atomic.AtomicInteger;
*/
public class Launcher extends Activity
implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener,
LauncherStateTransitionAnimation.Callbacks {
View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener {
static final String TAG = "Launcher";
static final boolean LOGD = false;
@ -452,7 +451,7 @@ public class Launcher extends Activity
mDragController = new DragController(this);
mInflater = getLayoutInflater();
mStateTransitionAnimation = new LauncherStateTransitionAnimation(this, this);
mStateTransitionAnimation = new LauncherStateTransitionAnimation(this);
mStats = new Stats(this);
@ -1844,7 +1843,7 @@ public class Launcher extends Activity
return mOverviewPanel;
}
public SearchDropTargetBar getSearchBar() {
public SearchDropTargetBar getSearchDropTargetBar() {
return mSearchDropTargetBar;
}
@ -3265,14 +3264,6 @@ public class Launcher extends Activity
}
}
@Override
public void onStateTransitionHideSearchBar() {
// Hide the search bar
if (mSearchDropTargetBar != null) {
mSearchDropTargetBar.hideSearchBar(false /* animated */);
}
}
public void showWorkspace(boolean animated) {
showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated, null);
}
@ -3290,16 +3281,9 @@ public class Launcher extends Activity
boolean changed = mState != State.WORKSPACE ||
mWorkspace.getState() != Workspace.State.NORMAL;
if (changed) {
boolean wasInSpringLoadedMode = (mState != State.WORKSPACE);
mWorkspace.setVisibility(View.VISIBLE);
mStateTransitionAnimation.startAnimationToWorkspace(mState, Workspace.State.NORMAL,
snapToPage, animated, onCompleteRunnable);
// Show the search bar (only animate if we were showing the drop target bar in spring
// loaded mode)
if (mSearchDropTargetBar != null) {
mSearchDropTargetBar.showSearchBar(animated && wasInSpringLoadedMode);
}
mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(),
Workspace.State.NORMAL, snapToPage, animated, onCompleteRunnable);
// Set focus to the AppsCustomize button
if (mAllAppsButton != null) {
@ -3323,7 +3307,8 @@ public class Launcher extends Activity
void showOverviewMode(boolean animated) {
mWorkspace.setVisibility(View.VISIBLE);
mStateTransitionAnimation.startAnimationToWorkspace(mState, Workspace.State.OVERVIEW,
mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(),
Workspace.State.OVERVIEW,
WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated,
null /* onCompleteRunnable */);
mState = State.WORKSPACE;
@ -3378,9 +3363,10 @@ public class Launcher extends Activity
}
if (toState == State.APPS) {
mStateTransitionAnimation.startAnimationToAllApps(animated, focusSearchBar);
mStateTransitionAnimation.startAnimationToAllApps(mWorkspace.getState(), animated,
focusSearchBar);
} else {
mStateTransitionAnimation.startAnimationToWidgets(animated);
mStateTransitionAnimation.startAnimationToWidgets(mWorkspace.getState(), animated);
}
// Change the state *after* we've called all the transition code
@ -3402,10 +3388,9 @@ public class Launcher extends Activity
* new state.
*/
public Animator startWorkspaceStateChangeAnimation(Workspace.State toState, int toPage,
boolean animated, boolean hasOverlaySearchBar, HashMap<View, Integer> layerViews) {
boolean animated, HashMap<View, Integer> layerViews) {
Workspace.State fromState = mWorkspace.getState();
Animator anim = mWorkspace.setStateWithAnimation(toState, toPage, animated,
hasOverlaySearchBar, layerViews);
Animator anim = mWorkspace.setStateWithAnimation(toState, toPage, animated, layerViews);
updateInteraction(fromState, toState);
return anim;
}
@ -3417,7 +3402,8 @@ public class Launcher extends Activity
return;
}
mStateTransitionAnimation.startAnimationToWorkspace(mState, Workspace.State.SPRING_LOADED,
mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(),
Workspace.State.SPRING_LOADED,
WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, true /* animated */,
null /* onCompleteRunnable */);
mState = isAppsViewVisible() ? State.APPS_SPRING_LOADED : State.WIDGETS_SPRING_LOADED;
@ -4517,14 +4503,16 @@ public class Launcher extends Activity
if (mWorkspace != null) mWorkspace.setAlpha(1f);
if (mHotseat != null) mHotseat.setAlpha(1f);
if (mPageIndicators != null) mPageIndicators.setAlpha(1f);
if (mSearchDropTargetBar != null) mSearchDropTargetBar.showSearchBar(false);
if (mSearchDropTargetBar != null) mSearchDropTargetBar.animateToState(
SearchDropTargetBar.State.SEARCH_BAR, 0);
}
void hideWorkspaceSearchAndHotseat() {
if (mWorkspace != null) mWorkspace.setAlpha(0f);
if (mHotseat != null) mHotseat.setAlpha(0f);
if (mPageIndicators != null) mPageIndicators.setAlpha(0f);
if (mSearchDropTargetBar != null) mSearchDropTargetBar.hideSearchBar(false);
if (mSearchDropTargetBar != null) mSearchDropTargetBar.animateToState(
SearchDropTargetBar.State.INVISIBLE, 0);
}
// TODO: These method should be a part of LauncherSearchCallback

View File

@ -79,13 +79,6 @@ import java.util.HashMap;
*/
public class LauncherStateTransitionAnimation {
/**
* Callbacks made during the state transition
*/
interface Callbacks {
public void onStateTransitionHideSearchBar();
}
/**
* Private callbacks made during transition setup.
*/
@ -111,12 +104,10 @@ public class LauncherStateTransitionAnimation {
public static final int SINGLE_FRAME_DELAY = 16;
@Thunk Launcher mLauncher;
@Thunk Callbacks mCb;
@Thunk AnimatorSet mStateAnimation;
@Thunk AnimatorSet mCurrentAnimation;
public LauncherStateTransitionAnimation(Launcher l, Callbacks cb) {
public LauncherStateTransitionAnimation(Launcher l) {
mLauncher = l;
mCb = cb;
}
/**
@ -125,8 +116,8 @@ public class LauncherStateTransitionAnimation {
* @param startSearchAfterTransition Immediately starts app search after the transition to
* All Apps is completed.
*/
public void startAnimationToAllApps(final boolean animated,
final boolean startSearchAfterTransition) {
public void startAnimationToAllApps(final Workspace.State fromWorkspaceState,
final boolean animated, final boolean startSearchAfterTransition) {
final AllAppsContainerView toView = mLauncher.getAppsView();
final View buttonView = mLauncher.getAllAppsButton();
PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
@ -159,15 +150,16 @@ public class LauncherStateTransitionAnimation {
}
};
// Only animate the search bar if animating from spring loaded mode back to all apps
startAnimationToOverlay(Workspace.State.NORMAL_HIDDEN, buttonView, toView,
toView.getContentView(), toView.getRevealView(), toView.getSearchBarView(),
animated, true /* hideSearchBar */, cb);
mCurrentAnimation = startAnimationToOverlay(fromWorkspaceState,
Workspace.State.NORMAL_HIDDEN, buttonView, toView, toView.getContentView(),
toView.getRevealView(), toView.getSearchBarView(), animated, cb);
}
/**
* Starts an animation to the widgets view.
*/
public void startAnimationToWidgets(final boolean animated) {
public void startAnimationToWidgets(final Workspace.State fromWorkspaceState,
final boolean animated) {
final WidgetsContainerView toView = mLauncher.getWidgetsView();
final View buttonView = mLauncher.getWidgetsButton();
@ -177,17 +169,17 @@ public class LauncherStateTransitionAnimation {
return 0.3f;
}
};
startAnimationToOverlay(Workspace.State.OVERVIEW_HIDDEN, buttonView, toView,
toView.getContentView(), toView.getRevealView(), null, animated,
true /* hideSearchBar */, cb);
mCurrentAnimation = startAnimationToOverlay(fromWorkspaceState,
Workspace.State.OVERVIEW_HIDDEN, buttonView, toView, toView.getContentView(),
toView.getRevealView(), null, animated, cb);
}
/**
* Starts and animation to the workspace from the current overlay view.
*/
public void startAnimationToWorkspace(final Launcher.State fromState,
final Workspace.State toWorkspaceState, final int toWorkspacePage,
final boolean animated, final Runnable onCompleteRunnable) {
final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState,
final int toWorkspacePage, final boolean animated, final Runnable onCompleteRunnable) {
if (toWorkspaceState != Workspace.State.NORMAL &&
toWorkspaceState != Workspace.State.SPRING_LOADED &&
toWorkspaceState != Workspace.State.OVERVIEW) {
@ -195,10 +187,10 @@ public class LauncherStateTransitionAnimation {
}
if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED) {
startAnimationToWorkspaceFromAllApps(toWorkspaceState, toWorkspacePage,
startAnimationToWorkspaceFromAllApps(fromWorkspaceState, toWorkspaceState, toWorkspacePage,
animated, onCompleteRunnable);
} else {
startAnimationToWorkspaceFromWidgets(toWorkspaceState, toWorkspacePage,
startAnimationToWorkspaceFromWidgets(fromWorkspaceState, toWorkspaceState, toWorkspacePage,
animated, onCompleteRunnable);
}
}
@ -207,10 +199,11 @@ public class LauncherStateTransitionAnimation {
* Creates and starts a new animation to a particular overlay view.
*/
@SuppressLint("NewApi")
private void startAnimationToOverlay(final Workspace.State toWorkspaceState,
final View buttonView, final View toView, final View contentView, final View revealView,
final View overlaySearchBarView, final boolean animated, final boolean hideSearchBar,
final PrivateTransitionCallbacks pCb) {
private AnimatorSet startAnimationToOverlay(final Workspace.State fromWorkspaceState,
final Workspace.State toWorkspaceState, final View buttonView, final View toView,
final View contentView, final View revealView, final View overlaySearchBarView,
final boolean animated, final PrivateTransitionCallbacks pCb) {
final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
final Resources res = mLauncher.getResources();
final boolean material = Utilities.isLmpOrAbove();
final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
@ -230,11 +223,13 @@ public class LauncherStateTransitionAnimation {
// Create the workspace animation.
// NOTE: this call apparently also sets the state for the workspace if !animated
Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState, -1,
animated, overlaySearchBarView != null /* hasOverlaySearchBar */, layerViews);
animated, layerViews);
// Animate the search bar
startWorkspaceSearchBarAnimation(animation, fromWorkspaceState, toWorkspaceState,
animated ? revealDuration : 0, overlaySearchBarView);
if (animated && initialized) {
mStateAnimation = LauncherAnimUtils.createAnimatorSet();
// Setup the reveal view animation
int width = revealView.getMeasuredWidth();
int height = revealView.getMeasuredHeight();
@ -274,7 +269,7 @@ public class LauncherStateTransitionAnimation {
// Play the animation
layerViews.put(revealView, BUILD_AND_SET_LAYER);
mStateAnimation.play(panelAlphaAndDrift);
animation.play(panelAlphaAndDrift);
if (overlaySearchBarView != null) {
overlaySearchBarView.setAlpha(0f);
@ -282,7 +277,7 @@ public class LauncherStateTransitionAnimation {
searchBarAlpha.setDuration(100);
searchBarAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
layerViews.put(overlaySearchBarView, BUILD_AND_SET_LAYER);
mStateAnimation.play(searchBarAlpha);
animation.play(searchBarAlpha);
}
// Setup the animation for the content view
@ -297,13 +292,13 @@ public class LauncherStateTransitionAnimation {
pageDrift.setDuration(revealDuration);
pageDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
pageDrift.setStartDelay(itemsAlphaStagger);
mStateAnimation.play(pageDrift);
animation.play(pageDrift);
ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 0f, 1f);
itemsAlpha.setDuration(revealDuration);
itemsAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
itemsAlpha.setStartDelay(itemsAlphaStagger);
mStateAnimation.play(itemsAlpha);
animation.play(itemsAlpha);
if (material) {
float startRadius = pCb.getMaterialRevealViewStartFinalRadius();
@ -316,10 +311,10 @@ public class LauncherStateTransitionAnimation {
if (listener != null) {
reveal.addListener(listener);
}
mStateAnimation.play(reveal);
animation.play(reveal);
}
mStateAnimation.addListener(new AnimatorListenerAdapter() {
animation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
dispatchOnLauncherTransitionEnd(fromView, animated, false);
@ -335,12 +330,8 @@ public class LauncherStateTransitionAnimation {
}
}
if (hideSearchBar) {
mCb.onStateTransitionHideSearchBar();
}
// This can hold unnecessary references to views.
mStateAnimation = null;
cleanupAnimation();
pCb.onTransitionComplete();
}
@ -348,7 +339,7 @@ public class LauncherStateTransitionAnimation {
// Play the workspace animation
if (workspaceAnim != null) {
mStateAnimation.play(workspaceAnim);
animation.play(workspaceAnim);
}
// Dispatch the prepare transition signal
@ -356,12 +347,12 @@ public class LauncherStateTransitionAnimation {
dispatchOnLauncherTransitionPrepare(toView, animated, false);
final AnimatorSet stateAnimation = mStateAnimation;
final AnimatorSet stateAnimation = animation;
final Runnable startAnimRunnable = new Runnable() {
public void run() {
// Check that mStateAnimation hasn't changed while
// Check that mCurrentAnimation hasn't changed while
// we waited for a layout/draw pass
if (mStateAnimation != stateAnimation)
if (mCurrentAnimation != stateAnimation)
return;
dispatchOnLauncherTransitionStart(fromView, animated, false);
dispatchOnLauncherTransitionStart(toView, animated, false);
@ -380,12 +371,14 @@ public class LauncherStateTransitionAnimation {
// Focus the new view
toView.requestFocus();
mStateAnimation.start();
stateAnimation.start();
}
};
toView.bringToFront();
toView.setVisibility(View.VISIBLE);
toView.post(startAnimRunnable);
return animation;
} else {
toView.setTranslationX(0.0f);
toView.setTranslationY(0.0f);
@ -397,10 +390,6 @@ public class LauncherStateTransitionAnimation {
// Show the content view
contentView.setVisibility(View.VISIBLE);
if (hideSearchBar) {
mCb.onStateTransitionHideSearchBar();
}
dispatchOnLauncherTransitionPrepare(fromView, animated, false);
dispatchOnLauncherTransitionStart(fromView, animated, false);
dispatchOnLauncherTransitionEnd(fromView, animated, false);
@ -408,18 +397,19 @@ public class LauncherStateTransitionAnimation {
dispatchOnLauncherTransitionStart(toView, animated, false);
dispatchOnLauncherTransitionEnd(toView, animated, false);
pCb.onTransitionComplete();
return null;
}
}
/**
* Starts and animation to the workspace from the apps view.
*/
private void startAnimationToWorkspaceFromAllApps(final Workspace.State toWorkspaceState,
final int toWorkspacePage, final boolean animated, final Runnable onCompleteRunnable) {
private void startAnimationToWorkspaceFromAllApps(final Workspace.State fromWorkspaceState,
final Workspace.State toWorkspaceState, final int toWorkspacePage,
final boolean animated, final Runnable onCompleteRunnable) {
AllAppsContainerView appsView = mLauncher.getAppsView();
PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
int[] mAllAppsToPanelDelta;
@Override
float getMaterialRevealViewFinalAlpha(View revealView) {
// No alpha anim from all apps
@ -451,8 +441,8 @@ public class LauncherStateTransitionAnimation {
}
};
// Only animate the search bar if animating to spring loaded mode from all apps
startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage,
mLauncher.getAllAppsButton(), appsView, appsView.getContentView(),
mCurrentAnimation = startAnimationToWorkspaceFromOverlay(fromWorkspaceState, toWorkspaceState,
toWorkspacePage, mLauncher.getAllAppsButton(), appsView, appsView.getContentView(),
appsView.getRevealView(), appsView.getSearchBarView(), animated,
onCompleteRunnable, cb);
}
@ -460,8 +450,9 @@ public class LauncherStateTransitionAnimation {
/**
* Starts and animation to the workspace from the widgets view.
*/
private void startAnimationToWorkspaceFromWidgets(final Workspace.State toWorkspaceState,
final int toWorkspacePage, final boolean animated, final Runnable onCompleteRunnable) {
private void startAnimationToWorkspaceFromWidgets(final Workspace.State fromWorkspaceState,
final Workspace.State toWorkspaceState, final int toWorkspacePage,
final boolean animated, final Runnable onCompleteRunnable) {
final WidgetsContainerView widgetsView = mLauncher.getWidgetsView();
PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
@Override
@ -479,19 +470,21 @@ public class LauncherStateTransitionAnimation {
};
}
};
startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage,
mLauncher.getWidgetsButton(), widgetsView, widgetsView.getContentView(),
widgetsView.getRevealView(), null, animated, onCompleteRunnable, cb);
mCurrentAnimation = startAnimationToWorkspaceFromOverlay(fromWorkspaceState,
toWorkspaceState, toWorkspacePage, mLauncher.getWidgetsButton(), widgetsView,
widgetsView.getContentView(), widgetsView.getRevealView(), null, animated,
onCompleteRunnable, cb);
}
/**
* Creates and starts a new animation to the workspace.
*/
private void startAnimationToWorkspaceFromOverlay(final Workspace.State toWorkspaceState,
final int toWorkspacePage, final View buttonView, final View fromView,
final View contentView, final View revealView, final View overlaySearchBarView,
final boolean animated, final Runnable onCompleteRunnable,
final PrivateTransitionCallbacks pCb) {
private AnimatorSet startAnimationToWorkspaceFromOverlay(final Workspace.State fromWorkspaceState,
final Workspace.State toWorkspaceState, final int toWorkspacePage, final View buttonView,
final View fromView, final View contentView, final View revealView,
final View overlaySearchBarView, final boolean animated, final Runnable onCompleteRunnable,
final PrivateTransitionCallbacks pCb) {
final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
final Resources res = mLauncher.getResources();
final boolean material = Utilities.isLmpOrAbove();
final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
@ -511,15 +504,16 @@ public class LauncherStateTransitionAnimation {
// Create the workspace animation.
// NOTE: this call apparently also sets the state for the workspace if !animated
Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState,
toWorkspacePage, animated, overlaySearchBarView != null /* hasOverlaySearchBar */,
layerViews);
toWorkspacePage, animated, layerViews);
// Animate the search bar
startWorkspaceSearchBarAnimation(animation, fromWorkspaceState, toWorkspaceState,
animated ? revealDuration : 0, overlaySearchBarView);
if (animated && initialized) {
mStateAnimation = LauncherAnimUtils.createAnimatorSet();
// Play the workspace animation
if (workspaceAnim != null) {
mStateAnimation.play(workspaceAnim);
animation.play(workspaceAnim);
}
// hideAppsCustomizeHelper is called in some cases when it is already hidden
@ -558,14 +552,14 @@ public class LauncherStateTransitionAnimation {
panelDriftY.setDuration(revealDuration - SINGLE_FRAME_DELAY);
panelDriftY.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
panelDriftY.setInterpolator(decelerateInterpolator);
mStateAnimation.play(panelDriftY);
animation.play(panelDriftY);
ObjectAnimator panelDriftX = ObjectAnimator.ofFloat(revealView, "translationX",
0, revealViewToXDrift);
panelDriftX.setDuration(revealDuration - SINGLE_FRAME_DELAY);
panelDriftX.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
panelDriftX.setInterpolator(decelerateInterpolator);
mStateAnimation.play(panelDriftX);
animation.play(panelDriftX);
// Setup animation for the reveal panel alpha
final float revealViewToAlpha = !material ? 0f :
@ -576,7 +570,7 @@ public class LauncherStateTransitionAnimation {
panelAlpha.setDuration(material ? revealDuration : 150);
panelAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY);
panelAlpha.setInterpolator(decelerateInterpolator);
mStateAnimation.play(panelAlpha);
animation.play(panelAlpha);
}
// Setup the animation for the content view
@ -589,13 +583,13 @@ public class LauncherStateTransitionAnimation {
pageDrift.setDuration(revealDuration - SINGLE_FRAME_DELAY);
pageDrift.setInterpolator(decelerateInterpolator);
pageDrift.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
mStateAnimation.play(pageDrift);
animation.play(pageDrift);
contentView.setAlpha(1f);
ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 1f, 0f);
itemsAlpha.setDuration(100);
itemsAlpha.setInterpolator(decelerateInterpolator);
mStateAnimation.play(itemsAlpha);
animation.play(itemsAlpha);
if (overlaySearchBarView != null) {
overlaySearchBarView.setAlpha(1f);
@ -604,7 +598,7 @@ public class LauncherStateTransitionAnimation {
searchAlpha.setInterpolator(decelerateInterpolator);
searchAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY);
layerViews.put(overlaySearchBarView, BUILD_AND_SET_LAYER);
mStateAnimation.play(searchAlpha);
animation.play(searchAlpha);
}
if (material) {
@ -620,14 +614,14 @@ public class LauncherStateTransitionAnimation {
if (listener != null) {
reveal.addListener(listener);
}
mStateAnimation.play(reveal);
animation.play(reveal);
}
dispatchOnLauncherTransitionPrepare(fromView, animated, true);
dispatchOnLauncherTransitionPrepare(toView, animated, true);
}
mStateAnimation.addListener(new AnimatorListenerAdapter() {
animation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
fromView.setVisibility(View.GONE);
@ -657,18 +651,19 @@ public class LauncherStateTransitionAnimation {
}
// This can hold unnecessary references to views.
mStateAnimation = null;
cleanupAnimation();
pCb.onTransitionComplete();
}
});
final AnimatorSet stateAnimation = mStateAnimation;
final AnimatorSet stateAnimation = animation;
final Runnable startAnimRunnable = new Runnable() {
public void run() {
// Check that mStateAnimation hasn't changed while
// Check that mCurrentAnimation hasn't changed while
// we waited for a layout/draw pass
if (mStateAnimation != stateAnimation)
if (mCurrentAnimation != stateAnimation)
return;
dispatchOnLauncherTransitionStart(fromView, animated, false);
dispatchOnLauncherTransitionStart(toView, animated, false);
@ -682,10 +677,12 @@ public class LauncherStateTransitionAnimation {
v.buildLayer();
}
}
mStateAnimation.start();
stateAnimation.start();
}
};
fromView.post(startAnimRunnable);
return animation;
} else {
fromView.setVisibility(View.GONE);
dispatchOnLauncherTransitionPrepare(fromView, animated, true);
@ -700,9 +697,44 @@ public class LauncherStateTransitionAnimation {
if (onCompleteRunnable != null) {
onCompleteRunnable.run();
}
return null;
}
}
/**
* Coordinates the workspace search bar animation along with the launcher state animation.
*/
private void startWorkspaceSearchBarAnimation(AnimatorSet animation,
final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState, int duration,
View overlaySearchBar) {
final SearchDropTargetBar.State toSearchBarState =
toWorkspaceState.getSearchDropTargetBarState();
if (overlaySearchBar != null) {
if ((toWorkspaceState == Workspace.State.NORMAL) &&
(fromWorkspaceState == Workspace.State.NORMAL_HIDDEN)) {
// If we are transitioning from the overlay to the workspace, then show the
// workspace search bar immediately and let the overlay search bar fade out on top
mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState, 0);
} else if (fromWorkspaceState == Workspace.State.NORMAL) {
// If we are transitioning from the workspace to the overlay, then keep the
// workspace search bar visible until the overlay search bar fades in on top
animation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState, 0);
}
});
} else {
// Otherwise, then just animate the workspace search bar normally
mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState, duration);
}
} else {
// If there is no overlay search bar, then just animate the workspace search bar
mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState, duration);
}
}
/**
* Dispatches the prepare-transition event to suitable views.
@ -753,10 +785,14 @@ public class LauncherStateTransitionAnimation {
* Cancels the current animation.
*/
private void cancelAnimation() {
if (mStateAnimation != null) {
mStateAnimation.setDuration(0);
mStateAnimation.cancel();
mStateAnimation = null;
if (mCurrentAnimation != null) {
mCurrentAnimation.setDuration(0);
mCurrentAnimation.cancel();
mCurrentAnimation = null;
}
}
private void cleanupAnimation() {
mCurrentAnimation = null;
}
}

View File

@ -26,6 +26,7 @@ import java.util.ArrayList;
import java.util.EnumSet;
public class LauncherViewPropertyAnimator extends Animator implements AnimatorListener {
enum Properties {
TRANSLATION_X,
TRANSLATION_Y,
@ -51,13 +52,12 @@ public class LauncherViewPropertyAnimator extends Animator implements AnimatorLi
long mStartDelay;
long mDuration;
TimeInterpolator mInterpolator;
ArrayList<Animator.AnimatorListener> mListeners;
ArrayList<Animator.AnimatorListener> mListeners = new ArrayList<>();
boolean mRunning = false;
FirstFrameAnimatorHelper mFirstFrameHelper;
public LauncherViewPropertyAnimator(View target) {
mTarget = target;
mListeners = new ArrayList<Animator.AnimatorListener>();
}
@Override

View File

@ -18,12 +18,11 @@ package com.android.launcher3;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.AccelerateInterpolator;
import android.widget.FrameLayout;
@ -33,17 +32,41 @@ import android.widget.FrameLayout;
*/
public class SearchDropTargetBar extends FrameLayout implements DragController.DragListener {
private static final int TRANSITION_DURATION = 200;
/** The different states that the search bar space can be in. */
public enum State {
INVISIBLE (0f, 0f),
SEARCH_BAR (1f, 0f),
DROP_TARGET (0f, 1f);
private ObjectAnimator mShowDropTargetBarAnim;
private ValueAnimator mHideSearchBarAnim;
private final float mSearchBarAlpha;
private final float mDropTargetBarAlpha;
State(float sbAlpha, float dtbAlpha) {
mSearchBarAlpha = sbAlpha;
mDropTargetBarAlpha = dtbAlpha;
}
float getSearchBarAlpha() {
return mSearchBarAlpha;
}
float getDropTargetBarAlpha() {
return mDropTargetBarAlpha;
}
}
private static int DEFAULT_DRAG_FADE_DURATION = 175;
private LauncherViewPropertyAnimator mDropTargetBarAnimator;
private LauncherViewPropertyAnimator mQSBSearchBarAnimator;
private static final AccelerateInterpolator sAccelerateInterpolator =
new AccelerateInterpolator();
private boolean mIsSearchBarHidden;
private View mQSBSearchBar;
private State mState = State.SEARCH_BAR;
private View mQSB;
private View mDropTargetBar;
private boolean mDeferOnDragEnd = false;
private boolean mAccessibilityEnabled = false;
// Drop targets
private ButtonDropTarget mInfoDropTarget;
@ -75,39 +98,6 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D
mUninstallDropTarget.setLauncher(launcher);
}
public void setQsbSearchBar(View qsb) {
mQSBSearchBar = qsb;
if (mQSBSearchBar != null) {
mHideSearchBarAnim = LauncherAnimUtils.ofFloat(mQSBSearchBar, "alpha", 1f, 0f);
setupAnimation(mHideSearchBarAnim, mQSBSearchBar);
} else {
// Create a no-op animation of the search bar is null
mHideSearchBarAnim = ValueAnimator.ofFloat(0, 0);
mHideSearchBarAnim.setDuration(TRANSITION_DURATION);
}
}
private void prepareStartAnimation(View v) {
// Enable the hw layers before the animation starts (will be disabled in the onAnimationEnd
// callback below)
if (v != null) {
v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
}
}
private void setupAnimation(ValueAnimator anim, final View v) {
anim.setInterpolator(sAccelerateInterpolator);
anim.setDuration(TRANSITION_DURATION);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (v != null) {
v.setLayerType(View.LAYER_TYPE_NONE, null);
}
}
});
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
@ -124,72 +114,84 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D
// Create the various fade animations
mDropTargetBar.setAlpha(0f);
mShowDropTargetBarAnim = LauncherAnimUtils.ofFloat(mDropTargetBar, "alpha", 0f, 1f);
setupAnimation(mShowDropTargetBarAnim, mDropTargetBar);
mDropTargetBarAnimator = new LauncherViewPropertyAnimator(mDropTargetBar);
mDropTargetBarAnimator.setInterpolator(sAccelerateInterpolator);
mDropTargetBarAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
// Ensure that the view is visible for the animation
mDropTargetBar.setVisibility(View.VISIBLE);
}
@Override
public void onAnimationEnd(Animator animation) {
if (mDropTargetBar != null) {
AlphaUpdateListener.updateVisibility(mDropTargetBar, mAccessibilityEnabled);
}
}
});
}
/**
* Finishes all the animations on the search and drop target bars.
*/
public void finishAnimations() {
prepareStartAnimation(mDropTargetBar);
mShowDropTargetBarAnim.reverse();
prepareStartAnimation(mQSBSearchBar);
mHideSearchBarAnim.reverse();
}
public void setQsbSearchBar(View qsb) {
mQSB = qsb;
if (mQSB != null) {
// Update the search ber animation
mQSBSearchBarAnimator = new LauncherViewPropertyAnimator(mQSB);
mQSBSearchBarAnimator.setInterpolator(sAccelerateInterpolator);
mQSBSearchBarAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
// Ensure that the view is visible for the animation
if (mQSB != null) {
mQSB.setVisibility(View.VISIBLE);
}
}
/**
* Shows the search bar.
*/
public void showSearchBar(boolean animated) {
if (!mIsSearchBarHidden) return;
if (animated) {
prepareStartAnimation(mQSBSearchBar);
mHideSearchBarAnim.reverse();
@Override
public void onAnimationEnd(Animator animation) {
if (mQSB != null) {
AlphaUpdateListener.updateVisibility(mQSB, mAccessibilityEnabled);
}
}
});
} else {
mHideSearchBarAnim.cancel();
if (mQSBSearchBar != null) {
mQSBSearchBar.setAlpha(1f);
mQSBSearchBarAnimator = null;
}
}
/**
* Animates the current search bar state to a new state. If the {@param duration} is 0, then
* the state is applied immediately.
*/
public void animateToState(State newState, int duration) {
if (mState != newState) {
mState = newState;
// Update the accessibility state
AccessibilityManager am = (AccessibilityManager)
getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
mAccessibilityEnabled = am.isEnabled();
animateViewAlpha(mQSBSearchBarAnimator, mQSB, newState.getSearchBarAlpha(),
duration);
animateViewAlpha(mDropTargetBarAnimator, mDropTargetBar, newState.getDropTargetBarAlpha(),
duration);
}
}
/**
* Convenience method to animate the alpha of a view using hardware layers.
*/
private void animateViewAlpha(LauncherViewPropertyAnimator animator, View v, float alpha,
int duration) {
if (v != null && Float.compare(v.getAlpha(), alpha) != 0) {
if (duration > 0) {
animator.alpha(alpha).withLayer().setDuration(duration).start();
} else {
v.setAlpha(alpha);
AlphaUpdateListener.updateVisibility(v, mAccessibilityEnabled);
}
}
mIsSearchBarHidden = false;
}
/**
* Hides the search bar. We only use this for clings.
*/
public void hideSearchBar(boolean animated) {
if (mIsSearchBarHidden) return;
if (animated) {
prepareStartAnimation(mQSBSearchBar);
mHideSearchBarAnim.start();
} else {
mHideSearchBarAnim.cancel();
if (mQSBSearchBar != null) {
mQSBSearchBar.setAlpha(0f);
}
}
mIsSearchBarHidden = true;
}
/**
* Shows the drop target bar.
*/
public void showDeleteTarget() {
// Animate out the QSB search bar, and animate in the drop target bar
prepareStartAnimation(mDropTargetBar);
mShowDropTargetBarAnim.start();
hideSearchBar(true);
}
/**
* Hides the drop target bar.
*/
public void hideDeleteTarget() {
// Restore the QSB search bar, and animate out the drop target bar
prepareStartAnimation(mDropTargetBar);
mShowDropTargetBarAnim.reverse();
showSearchBar(true);
}
/*
@ -197,9 +199,13 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D
*/
@Override
public void onDragStart(DragSource source, Object info, int dragAction) {
showDeleteTarget();
animateToState(State.DROP_TARGET, DEFAULT_DRAG_FADE_DURATION);
}
/**
* This is called to defer hiding the delete drop target until the drop animation has completed,
* instead of hiding immediately when the drag has ended.
*/
public void deferOnDragEnd() {
mDeferOnDragEnd = true;
}
@ -207,22 +213,25 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D
@Override
public void onDragEnd() {
if (!mDeferOnDragEnd) {
hideDeleteTarget();
animateToState(State.SEARCH_BAR, DEFAULT_DRAG_FADE_DURATION);
} else {
mDeferOnDragEnd = false;
}
}
/**
* @return the bounds of the QSB search bar.
*/
public Rect getSearchBarBounds() {
if (mQSBSearchBar != null) {
if (mQSB != null) {
final int[] pos = new int[2];
mQSBSearchBar.getLocationOnScreen(pos);
mQSB.getLocationOnScreen(pos);
final Rect rect = new Rect();
rect.left = pos[0];
rect.top = pos[1];
rect.right = pos[0] + mQSBSearchBar.getWidth();
rect.bottom = pos[1] + mQSBSearchBar.getHeight();
rect.right = pos[0] + mQSB.getWidth();
rect.bottom = pos[1] + mQSB.getHeight();
return rect;
} else {
return null;
@ -230,8 +239,8 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D
}
public void enableAccessibleDrag(boolean enable) {
if (mQSBSearchBar != null) {
mQSBSearchBar.setVisibility(enable ? View.GONE : View.VISIBLE);
if (mQSB != null) {
mQSB.setVisibility(enable ? View.GONE : View.VISIBLE);
}
mInfoDropTarget.enableAccessibleDrag(enable);
mDeleteDropTarget.enableAccessibleDrag(enable);

View File

@ -178,7 +178,24 @@ public class Workspace extends PagedView
// State variable that indicates whether the pages are small (ie when you're
// in all apps or customize mode)
enum State { NORMAL, NORMAL_HIDDEN, SPRING_LOADED, OVERVIEW, OVERVIEW_HIDDEN};
enum State {
NORMAL (SearchDropTargetBar.State.SEARCH_BAR),
NORMAL_HIDDEN (SearchDropTargetBar.State.INVISIBLE),
SPRING_LOADED (SearchDropTargetBar.State.DROP_TARGET),
OVERVIEW (SearchDropTargetBar.State.INVISIBLE),
OVERVIEW_HIDDEN (SearchDropTargetBar.State.INVISIBLE);
private final SearchDropTargetBar.State mBarState;
State(SearchDropTargetBar.State searchBarState) {
mBarState = searchBarState;
}
public SearchDropTargetBar.State getSearchDropTargetBarState() {
return mBarState;
}
};
private State mState = State.NORMAL;
private boolean mIsSwitchingState = false;
@ -1567,7 +1584,7 @@ public class Workspace extends PagedView
// Reset our click listener
setOnClickListener(mLauncher);
}
mLauncher.getSearchBar().enableAccessibleDrag(enable);
mLauncher.getSearchDropTargetBar().enableAccessibleDrag(enable);
mLauncher.getHotseat().getLayout()
.enableAccessibleDrag(enable, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
}
@ -1971,10 +1988,10 @@ public class Workspace extends PagedView
* to that new state.
*/
public Animator setStateWithAnimation(State toState, int toPage, boolean animated,
boolean hasOverlaySearchBar, HashMap<View, Integer> layerViews) {
HashMap<View, Integer> layerViews) {
// Create the animation to the new state
Animator workspaceAnim = mStateTransitionAnimation.getAnimationToState(mState,
toState, toPage, animated, hasOverlaySearchBar, layerViews);
toState, toPage, animated, layerViews);
// Update the current state
mState = toState;

View File

@ -218,16 +218,13 @@ public class WorkspaceStateTransitionAnimation {
}
public AnimatorSet getAnimationToState(Workspace.State fromState, Workspace.State toState,
int toPage, boolean animated, boolean hasOverlaySearchBar,
HashMap<View, Integer> layerViews) {
int toPage, boolean animated, HashMap<View, Integer> layerViews) {
AccessibilityManager am = (AccessibilityManager)
mLauncher.getSystemService(Context.ACCESSIBILITY_SERVICE);
final boolean accessibilityEnabled = am.isEnabled();
TransitionStates states = new TransitionStates(fromState, toState);
int duration = getAnimationDuration(states);
animateWorkspace(states, toPage, animated, duration, layerViews,
accessibilityEnabled);
animateSearchBar(states, animated, duration, hasOverlaySearchBar, layerViews,
int workspaceDuration = getAnimationDuration(states);
animateWorkspace(states, toPage, animated, workspaceDuration, layerViews,
accessibilityEnabled);
animateBackgroundGradient(states, animated, BACKGROUND_FADE_OUT_DURATION);
return mStateAnimator;
@ -473,76 +470,10 @@ public class WorkspaceStateTransitionAnimation {
}
}
/**
* Coordinates with the workspace animation to animate the search bar.
*
* TODO: This should really be coordinated with the SearchDropTargetBar, otherwise the
* bar has no idea that it is hidden, and this has no idea what state the bar is
* actually in.
*/
private void animateSearchBar(TransitionStates states, boolean animated, int duration,
boolean hasOverlaySearchBar, final HashMap<View, Integer> layerViews,
final boolean accessibilityEnabled) {
// The search bar is only visible in the workspace
final View searchBar = mLauncher.getOrCreateQsbBar();
if (searchBar != null) {
final boolean searchBarWillBeShown = states.stateIsNormal;
final float finalSearchBarAlpha = searchBarWillBeShown ? 1f : 0f;
if (animated) {
if (hasOverlaySearchBar) {
// If there is an overlay search bar, then we will coordinate with it.
mStateAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
// If we are transitioning to a visible search bar, show it immediately
// and let the overlay search bar has faded out
if (searchBarWillBeShown) {
searchBar.setAlpha(finalSearchBarAlpha);
AlphaUpdateListener.updateVisibility(searchBar, accessibilityEnabled);
}
}
@Override
public void onAnimationEnd(Animator animation) {
// If we are transitioning to a hidden search bar, hide it only after
// the overlay search bar has faded in
if (!searchBarWillBeShown) {
searchBar.setAlpha(finalSearchBarAlpha);
AlphaUpdateListener.updateVisibility(searchBar, accessibilityEnabled);
}
}
});
} else {
// Otherwise, we can just do the normal animation
LauncherViewPropertyAnimator searchBarAlpha =
new LauncherViewPropertyAnimator(searchBar).alpha(finalSearchBarAlpha);
searchBarAlpha.addListener(new AlphaUpdateListener(searchBar,
accessibilityEnabled));
searchBar.setLayerType(View.LAYER_TYPE_HARDWARE, null);
if (layerViews != null) {
// If layerViews is not null, we add these views, and indicate that
// the caller can manage layer state.
layerViews.put(searchBar, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER);
} else {
// Otherwise let the animator handle layer management.
searchBarAlpha.withLayer();
}
searchBarAlpha.setDuration(duration);
mStateAnimator.play(searchBarAlpha);
}
} else {
// Set the search bar state immediately
searchBar.setAlpha(finalSearchBarAlpha);
AlphaUpdateListener.updateVisibility(searchBar, accessibilityEnabled);
}
}
}
/**
* Animates the background scrim. Add to the state animator to prevent jankiness.
*
* @param finalAlpha the final alpha for the background scrim
* @param states the current and final workspace states
* @param animated whether or not to set the background alpha immediately
* @duration duration of the animation
*/