blob: 0ea9d8bd072a13b6e425d519ce36d9524efa412a [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.TimeInterpolator;
import android.annotation.SuppressLint;
import android.content.res.Resources;
import android.util.Log;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.util.UiThreadCircularReveal;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.widget.WidgetsContainerView;
import java.util.HashMap;
/**
* TODO: figure out what kind of tests we can write for this
*
* Things to test when changing the following class.
* - Home from workspace
* - from center screen
* - from other screens
* - Home from all apps
* - from center screen
* - from other screens
* - Back from all apps
* - from center screen
* - from other screens
* - Launch app from workspace and quit
* - with back
* - with home
* - Launch app from all apps and quit
* - with back
* - with home
* - Go to a screen that's not the default, then all
* apps, and launch and app, and go back
* - with back
* -with home
* - On workspace, long press power and go back
* - with back
* - with home
* - On all apps, long press power and go back
* - with back
* - with home
* - On workspace, power off
* - On all apps, power off
* - Launch an app and turn off the screen while in that app
* - Go back with home key
* - Go back with back key TODO: make this not go to workspace
* - From all apps
* - From workspace
* - Enter and exit car mode (becuase it causes an extra configuration changed)
* - From all apps
* - From the center workspace
* - From another workspace
*/
public class LauncherStateTransitionAnimation {
/**
* Callbacks made during the state transition
*/
interface Callbacks {
public void onStateTransitionHideSearchBar();
}
/**
* Private callbacks made during transition setup.
*/
static abstract class PrivateTransitionCallbacks {
void onRevealViewVisible(View revealView, View contentView, View allAppsButtonView) {}
float getMaterialRevealViewFinalAlpha(View revealView) {
return 0;
}
float getMaterialRevealViewFinalXDrift(View revealView) {
return 0;
}
float getMaterialRevealViewFinalYDrift(View revealView) {
return 0;
}
float getMaterialRevealViewStartFinalRadius() {
return 0;
}
AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(View revealView,
View allAppsButtonView) {
return null;
}
void onTransitionComplete() {}
}
public static final String TAG = "LauncherStateTransitionAnimation";
// Flags to determine how to set the layers on views before the transition animation
public static final int BUILD_LAYER = 0;
public static final int BUILD_AND_SET_LAYER = 1;
public static final int SINGLE_FRAME_DELAY = 16;
@Thunk Launcher mLauncher;
@Thunk Callbacks mCb;
@Thunk AnimatorSet mStateAnimation;
public LauncherStateTransitionAnimation(Launcher l, Callbacks cb) {
mLauncher = l;
mCb = cb;
}
/**
* Starts an animation to the apps view.
*
* @param startSearchAfterTransition Immediately starts app search after the transition to
* All Apps is completed.
*/
public void startAnimationToAllApps(final boolean animated,
final boolean startSearchAfterTransition) {
final AllAppsContainerView toView = mLauncher.getAppsView();
PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
private int[] mAllAppsToPanelDelta;
@Override
public void onRevealViewVisible(View revealView, View contentView,
View allAppsButtonView) {
// Get the y delta between the center of the page and the center of the all apps
// button
mAllAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
allAppsButtonView, null);
}
@Override
public float getMaterialRevealViewFinalAlpha(View revealView) {
return 1f;
}
@Override
public float getMaterialRevealViewFinalXDrift(View revealView) {
return mAllAppsToPanelDelta[0];
}
@Override
public float getMaterialRevealViewFinalYDrift(View revealView) {
return mAllAppsToPanelDelta[1];
}
@Override
public float getMaterialRevealViewStartFinalRadius() {
int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize;
return allAppsButtonSize / 2;
}
@Override
public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
final View revealView, final View allAppsButtonView) {
return new AnimatorListenerAdapter() {
public void onAnimationStart(Animator animation) {
allAppsButtonView.setVisibility(View.INVISIBLE);
}
public void onAnimationEnd(Animator animation) {
allAppsButtonView.setVisibility(View.VISIBLE);
}
};
}
@Override
void onTransitionComplete() {
if (startSearchAfterTransition) {
toView.startAppsSearch();
}
}
};
// Only animate the search bar if animating from spring loaded mode back to all apps
startAnimationToOverlay(Workspace.State.NORMAL_HIDDEN, toView, toView.getContentView(),
toView.getRevealView(), toView.getSearchBarView(), animated,
true /* hideSearchBar */, cb);
}
/**
* Starts an animation to the widgets view.
*/
public void startAnimationToWidgets(final boolean animated) {
final WidgetsContainerView toView = mLauncher.getWidgetsView();
final Resources res = mLauncher.getResources();
PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
@Override
public void onRevealViewVisible(View revealView, View contentView,
View allAppsButtonView) {
}
@Override
public float getMaterialRevealViewFinalAlpha(View revealView) {
return 0.3f;
}
@Override
public float getMaterialRevealViewFinalYDrift(View revealView) {
return revealView.getMeasuredHeight() / 2;
}
};
startAnimationToOverlay(Workspace.State.OVERVIEW_HIDDEN, toView,
toView.getContentView(), toView.getRevealView(), null, animated,
true /* hideSearchBar */, 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) {
if (toWorkspaceState != Workspace.State.NORMAL &&
toWorkspaceState != Workspace.State.SPRING_LOADED &&
toWorkspaceState != Workspace.State.OVERVIEW) {
Log.e(TAG, "Unexpected call to startAnimationToWorkspace");
}
if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED) {
startAnimationToWorkspaceFromAllApps(toWorkspaceState, toWorkspacePage,
animated, onCompleteRunnable);
} else {
startAnimationToWorkspaceFromWidgets(toWorkspaceState, toWorkspacePage,
animated, onCompleteRunnable);
}
}
/**
* Creates and starts a new animation to a particular overlay view.
*/
@SuppressLint("NewApi")
private void startAnimationToOverlay(final Workspace.State toWorkspaceState, final View toView,
final View contentView, final View revealView, final View overlaySearchBarView,
final boolean animated, final boolean hideSearchBar,
final PrivateTransitionCallbacks pCb) {
final Resources res = mLauncher.getResources();
final boolean material = Utilities.isLmpOrAbove();
final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
final int itemsAlphaStagger =
res.getInteger(R.integer.config_overlayItemsAlphaStagger);
final View allAppsButtonView = mLauncher.getAllAppsButton();
final View fromView = mLauncher.getWorkspace();
final HashMap<View, Integer> layerViews = new HashMap<>();
// If for some reason our views aren't initialized, don't animate
boolean initialized = allAppsButtonView != null;
// Cancel the current animation
cancelAnimation();
// 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);
if (animated && initialized) {
mStateAnimation = LauncherAnimUtils.createAnimatorSet();
// Setup the reveal view animation
int width = revealView.getMeasuredWidth();
int height = revealView.getMeasuredHeight();
float revealRadius = (float) Math.hypot(width / 2, height / 2);
revealView.setVisibility(View.VISIBLE);
revealView.setAlpha(0f);
revealView.setTranslationY(0f);
revealView.setTranslationX(0f);
pCb.onRevealViewVisible(revealView, contentView, allAppsButtonView);
// Calculate the final animation values
final float revealViewToAlpha;
final float revealViewToXDrift;
final float revealViewToYDrift;
if (material) {
revealViewToAlpha = pCb.getMaterialRevealViewFinalAlpha(revealView);
revealViewToYDrift = pCb.getMaterialRevealViewFinalYDrift(revealView);
revealViewToXDrift = pCb.getMaterialRevealViewFinalXDrift(revealView);
} else {
revealViewToAlpha = 0f;
revealViewToYDrift = 2 * height / 3;
revealViewToXDrift = 0;
}
// Create the animators
PropertyValuesHolder panelAlpha =
PropertyValuesHolder.ofFloat("alpha", revealViewToAlpha, 1f);
PropertyValuesHolder panelDriftY =
PropertyValuesHolder.ofFloat("translationY", revealViewToYDrift, 0);
PropertyValuesHolder panelDriftX =
PropertyValuesHolder.ofFloat("translationX", revealViewToXDrift, 0);
ObjectAnimator panelAlphaAndDrift = ObjectAnimator.ofPropertyValuesHolder(revealView,
panelAlpha, panelDriftY, panelDriftX);
panelAlphaAndDrift.setDuration(revealDuration);
panelAlphaAndDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
// Play the animation
layerViews.put(revealView, BUILD_AND_SET_LAYER);
mStateAnimation.play(panelAlphaAndDrift);
if (overlaySearchBarView != null) {
overlaySearchBarView.setAlpha(0f);
ObjectAnimator searchBarAlpha = ObjectAnimator.ofFloat(overlaySearchBarView, "alpha", 0f, 1f);
searchBarAlpha.setDuration(100);
searchBarAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
layerViews.put(overlaySearchBarView, BUILD_AND_SET_LAYER);
mStateAnimation.play(searchBarAlpha);
}
// Setup the animation for the content view
contentView.setVisibility(View.VISIBLE);
contentView.setAlpha(0f);
contentView.setTranslationY(revealViewToYDrift);
layerViews.put(contentView, BUILD_AND_SET_LAYER);
// Create the individual animators
ObjectAnimator pageDrift = ObjectAnimator.ofFloat(contentView, "translationY",
revealViewToYDrift, 0);
pageDrift.setDuration(revealDuration);
pageDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
pageDrift.setStartDelay(itemsAlphaStagger);
mStateAnimation.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);
if (material) {
// Animate the all apps button
float startRadius = pCb.getMaterialRevealViewStartFinalRadius();
AnimatorListenerAdapter listener = pCb.getMaterialRevealViewAnimatorListener(
revealView, allAppsButtonView);
Animator reveal = UiThreadCircularReveal.createCircularReveal(revealView, width / 2,
height / 2, startRadius, revealRadius);
reveal.setDuration(revealDuration);
reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
if (listener != null) {
reveal.addListener(listener);
}
mStateAnimation.play(reveal);
}
mStateAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
dispatchOnLauncherTransitionEnd(fromView, animated, false);
dispatchOnLauncherTransitionEnd(toView, animated, false);
// Hide the reveal view
revealView.setVisibility(View.INVISIBLE);
// Disable all necessary layers
for (View v : layerViews.keySet()) {
if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
v.setLayerType(View.LAYER_TYPE_NONE, null);
}
}
if (hideSearchBar) {
mCb.onStateTransitionHideSearchBar();
}
// This can hold unnecessary references to views.
mStateAnimation = null;
pCb.onTransitionComplete();
}
});
// Play the workspace animation
if (workspaceAnim != null) {
mStateAnimation.play(workspaceAnim);
}
// Dispatch the prepare transition signal
dispatchOnLauncherTransitionPrepare(fromView, animated, false);
dispatchOnLauncherTransitionPrepare(toView, animated, false);
final AnimatorSet stateAnimation = mStateAnimation;
final Runnable startAnimRunnable = new Runnable() {
public void run() {
// Check that mStateAnimation hasn't changed while
// we waited for a layout/draw pass
if (mStateAnimation != stateAnimation)
return;
dispatchOnLauncherTransitionStart(fromView, animated, false);
dispatchOnLauncherTransitionStart(toView, animated, false);
// Enable all necessary layers
boolean isLmpOrAbove = Utilities.isLmpOrAbove();
for (View v : layerViews.keySet()) {
if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
}
if (isLmpOrAbove && Utilities.isViewAttachedToWindow(v)) {
v.buildLayer();
}
}
// Focus the new view
toView.requestFocus();
mStateAnimation.start();
}
};
toView.bringToFront();
toView.setVisibility(View.VISIBLE);
toView.post(startAnimRunnable);
} else {
toView.setTranslationX(0.0f);
toView.setTranslationY(0.0f);
toView.setScaleX(1.0f);
toView.setScaleY(1.0f);
toView.setVisibility(View.VISIBLE);
toView.bringToFront();
// Show the content view
contentView.setVisibility(View.VISIBLE);
if (hideSearchBar) {
mCb.onStateTransitionHideSearchBar();
}
dispatchOnLauncherTransitionPrepare(fromView, animated, false);
dispatchOnLauncherTransitionStart(fromView, animated, false);
dispatchOnLauncherTransitionEnd(fromView, animated, false);
dispatchOnLauncherTransitionPrepare(toView, animated, false);
dispatchOnLauncherTransitionStart(toView, animated, false);
dispatchOnLauncherTransitionEnd(toView, animated, false);
pCb.onTransitionComplete();
}
}
/**
* 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) {
AllAppsContainerView appsView = mLauncher.getAppsView();
PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
int[] mAllAppsToPanelDelta;
@Override
public void onRevealViewVisible(View revealView, View contentView,
View allAppsButtonView) {
// Get the y delta between the center of the page and the center of the all apps
// button
mAllAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
allAppsButtonView, null);
}
@Override
public float getMaterialRevealViewFinalXDrift(View revealView) {
return mAllAppsToPanelDelta[0];
}
@Override
public float getMaterialRevealViewFinalYDrift(View revealView) {
return mAllAppsToPanelDelta[1];
}
@Override
float getMaterialRevealViewFinalAlpha(View revealView) {
// No alpha anim from all apps
return 1f;
}
@Override
float getMaterialRevealViewStartFinalRadius() {
int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize;
return allAppsButtonSize / 2;
}
@Override
public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
final View revealView, final View allAppsButtonView) {
return new AnimatorListenerAdapter() {
public void onAnimationStart(Animator animation) {
// We set the alpha instead of visibility to ensure that the focus does not
// get taken from the all apps view
allAppsButtonView.setVisibility(View.VISIBLE);
allAppsButtonView.setAlpha(0f);
}
public void onAnimationEnd(Animator animation) {
// Hide the reveal view
revealView.setVisibility(View.INVISIBLE);
// Show the all apps button, and focus it
allAppsButtonView.setAlpha(1f);
}
};
}
};
// Only animate the search bar if animating to spring loaded mode from all apps
startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage, appsView,
appsView.getContentView(), appsView.getRevealView(), appsView.getSearchBarView(),
animated, onCompleteRunnable, cb);
}
/**
* 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) {
final WidgetsContainerView widgetsView = mLauncher.getWidgetsView();
final Resources res = mLauncher.getResources();
PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
@Override
public void onRevealViewVisible(View revealView, View contentView,
View allAppsButtonView) {
}
@Override
public float getMaterialRevealViewFinalYDrift(View revealView) {
return revealView.getMeasuredHeight() / 2;
}
@Override
float getMaterialRevealViewFinalAlpha(View revealView) {
return 0.4f;
}
@Override
public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
final View revealView, final View allAppsButtonView) {
return new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator animation) {
// Hide the reveal view
revealView.setVisibility(View.INVISIBLE);
}
};
}
};
startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage, 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 fromView, final View contentView,
final View revealView, final View overlaySearchBarView, final boolean animated,
final Runnable onCompleteRunnable, final PrivateTransitionCallbacks pCb) {
final Resources res = mLauncher.getResources();
final boolean material = Utilities.isLmpOrAbove();
final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
final int itemsAlphaStagger =
res.getInteger(R.integer.config_overlayItemsAlphaStagger);
final View allAppsButtonView = mLauncher.getAllAppsButton();
final View toView = mLauncher.getWorkspace();
final HashMap<View, Integer> layerViews = new HashMap<>();
// If for some reason our views aren't initialized, don't animate
boolean initialized = allAppsButtonView != null;
// Cancel the current animation
cancelAnimation();
// 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);
if (animated && initialized) {
mStateAnimation = LauncherAnimUtils.createAnimatorSet();
// Play the workspace animation
if (workspaceAnim != null) {
mStateAnimation.play(workspaceAnim);
}
// hideAppsCustomizeHelper is called in some cases when it is already hidden
// don't perform all these no-op animations. In particularly, this was causing
// the all-apps button to pop in and out.
if (fromView.getVisibility() == View.VISIBLE) {
int width = revealView.getMeasuredWidth();
int height = revealView.getMeasuredHeight();
float revealRadius = (float) Math.hypot(width / 2, height / 2);
revealView.setVisibility(View.VISIBLE);
revealView.setAlpha(1f);
revealView.setTranslationY(0);
layerViews.put(revealView, BUILD_AND_SET_LAYER);
pCb.onRevealViewVisible(revealView, contentView, allAppsButtonView);
// Calculate the final animation values
final float revealViewToXDrift;
final float revealViewToYDrift;
if (material) {
revealViewToYDrift = pCb.getMaterialRevealViewFinalYDrift(revealView);
revealViewToXDrift = pCb.getMaterialRevealViewFinalXDrift(revealView);
} else {
revealViewToYDrift = 2 * height / 3;
revealViewToXDrift = 0;
}
// The vertical motion of the apps panel should be delayed by one frame
// from the conceal animation in order to give the right feel. We correspondingly
// shorten the duration so that the slide and conceal end at the same time.
TimeInterpolator decelerateInterpolator = material ?
new LogDecelerateInterpolator(100, 0) :
new DecelerateInterpolator(1f);
ObjectAnimator panelDriftY = ObjectAnimator.ofFloat(revealView, "translationY",
0, revealViewToYDrift);
panelDriftY.setDuration(revealDuration - SINGLE_FRAME_DELAY);
panelDriftY.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
panelDriftY.setInterpolator(decelerateInterpolator);
mStateAnimation.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);
// Setup animation for the reveal panel alpha
final float revealViewToAlpha = !material ? 0f :
pCb.getMaterialRevealViewFinalAlpha(revealView);
if (revealViewToAlpha != 1f) {
ObjectAnimator panelAlpha = ObjectAnimator.ofFloat(revealView, "alpha",
1f, revealViewToAlpha);
panelAlpha.setDuration(material ? revealDuration : 150);
panelAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY);
panelAlpha.setInterpolator(decelerateInterpolator);
mStateAnimation.play(panelAlpha);
}
// Setup the animation for the content view
layerViews.put(contentView, BUILD_AND_SET_LAYER);
// Create the individual animators
ObjectAnimator pageDrift = ObjectAnimator.ofFloat(contentView, "translationY",
0, revealViewToYDrift);
contentView.setTranslationY(0);
pageDrift.setDuration(revealDuration - SINGLE_FRAME_DELAY);
pageDrift.setInterpolator(decelerateInterpolator);
pageDrift.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
mStateAnimation.play(pageDrift);
contentView.setAlpha(1f);
ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 1f, 0f);
itemsAlpha.setDuration(100);
itemsAlpha.setInterpolator(decelerateInterpolator);
mStateAnimation.play(itemsAlpha);
if (overlaySearchBarView != null) {
overlaySearchBarView.setAlpha(1f);
ObjectAnimator searchAlpha = ObjectAnimator.ofFloat(overlaySearchBarView, "alpha", 1f, 0f);
searchAlpha.setDuration(material ? 100 : 150);
searchAlpha.setInterpolator(decelerateInterpolator);
searchAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY);
layerViews.put(overlaySearchBarView, BUILD_AND_SET_LAYER);
mStateAnimation.play(searchAlpha);
}
if (material) {
// Animate the all apps button
float finalRadius = pCb.getMaterialRevealViewStartFinalRadius();
AnimatorListenerAdapter listener =
pCb.getMaterialRevealViewAnimatorListener(revealView, allAppsButtonView);
Animator reveal = UiThreadCircularReveal.createCircularReveal(revealView, width / 2,
height / 2, revealRadius, finalRadius);
reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
reveal.setDuration(revealDuration);
reveal.setStartDelay(itemsAlphaStagger);
if (listener != null) {
reveal.addListener(listener);
}
mStateAnimation.play(reveal);
}
dispatchOnLauncherTransitionPrepare(fromView, animated, true);
dispatchOnLauncherTransitionPrepare(toView, animated, true);
}
mStateAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
fromView.setVisibility(View.GONE);
dispatchOnLauncherTransitionEnd(fromView, animated, true);
dispatchOnLauncherTransitionEnd(toView, animated, true);
// Run any queued runnables
if (onCompleteRunnable != null) {
onCompleteRunnable.run();
}
// Disable all necessary layers
for (View v : layerViews.keySet()) {
if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
v.setLayerType(View.LAYER_TYPE_NONE, null);
}
}
// Reset page transforms
if (contentView != null) {
contentView.setTranslationX(0);
contentView.setTranslationY(0);
contentView.setAlpha(1);
}
if (overlaySearchBarView != null) {
overlaySearchBarView.setAlpha(1f);
}
// This can hold unnecessary references to views.
mStateAnimation = null;
pCb.onTransitionComplete();
}
});
final AnimatorSet stateAnimation = mStateAnimation;
final Runnable startAnimRunnable = new Runnable() {
public void run() {
// Check that mStateAnimation hasn't changed while
// we waited for a layout/draw pass
if (mStateAnimation != stateAnimation)
return;
dispatchOnLauncherTransitionStart(fromView, animated, false);
dispatchOnLauncherTransitionStart(toView, animated, false);
// Enable all necessary layers
boolean isLmpOrAbove = Utilities.isLmpOrAbove();
for (View v : layerViews.keySet()) {
if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
}
if (isLmpOrAbove && Utilities.isViewAttachedToWindow(v)) {
v.buildLayer();
}
}
mStateAnimation.start();
}
};
fromView.post(startAnimRunnable);
} else {
fromView.setVisibility(View.GONE);
dispatchOnLauncherTransitionPrepare(fromView, animated, true);
dispatchOnLauncherTransitionStart(fromView, animated, true);
dispatchOnLauncherTransitionEnd(fromView, animated, true);
dispatchOnLauncherTransitionPrepare(toView, animated, true);
dispatchOnLauncherTransitionStart(toView, animated, true);
dispatchOnLauncherTransitionEnd(toView, animated, true);
pCb.onTransitionComplete();
// Run any queued runnables
if (onCompleteRunnable != null) {
onCompleteRunnable.run();
}
}
}
/**
* Dispatches the prepare-transition event to suitable views.
*/
void dispatchOnLauncherTransitionPrepare(View v, boolean animated, boolean toWorkspace) {
if (v instanceof LauncherTransitionable) {
((LauncherTransitionable) v).onLauncherTransitionPrepare(mLauncher, animated,
toWorkspace);
}
}
/**
* Dispatches the start-transition event to suitable views.
*/
void dispatchOnLauncherTransitionStart(View v, boolean animated, boolean toWorkspace) {
if (v instanceof LauncherTransitionable) {
((LauncherTransitionable) v).onLauncherTransitionStart(mLauncher, animated,
toWorkspace);
}
// Update the workspace transition step as well
dispatchOnLauncherTransitionStep(v, 0f);
}
/**
* Dispatches the step-transition event to suitable views.
*/
void dispatchOnLauncherTransitionStep(View v, float t) {
if (v instanceof LauncherTransitionable) {
((LauncherTransitionable) v).onLauncherTransitionStep(mLauncher, t);
}
}
/**
* Dispatches the end-transition event to suitable views.
*/
void dispatchOnLauncherTransitionEnd(View v, boolean animated, boolean toWorkspace) {
if (v instanceof LauncherTransitionable) {
((LauncherTransitionable) v).onLauncherTransitionEnd(mLauncher, animated,
toWorkspace);
}
// Update the workspace transition step as well
dispatchOnLauncherTransitionStep(v, 1f);
}
/**
* Cancels the current animation.
*/
private void cancelAnimation() {
if (mStateAnimation != null) {
mStateAnimation.setDuration(0);
mStateAnimation.cancel();
mStateAnimation = null;
}
}
}