/* * 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) {} void onAnimationComplete(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; } } 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. */ public void startAnimationToAllApps(final boolean animated) { final AllAppsContainerView toView = mLauncher.getAppsView(); PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { private int[] mAllAppsToPanelDelta; @Override public void onRevealViewVisible(View revealView, View contentView, View allAppsButtonView) { toView.setBackground(null); // 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); } }; } }; startAnimationToOverlay(Workspace.State.NORMAL_HIDDEN, toView, toView.getContentView(), toView.getRevealView(), animated, !mLauncher.isAllAppsSearchOverridden() /* 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) { revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark)); } @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(), 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(fromState, toWorkspaceState, toWorkspacePage, animated, onCompleteRunnable); } else { startAnimationToWorkspaceFromWidgets(fromState, 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 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 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, 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); // 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); pCb.onAnimationComplete(revealView, contentView, allAppsButtonView); // 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; } }); // 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); } } /** * Starts and animation to the workspace from the apps view. */ private void startAnimationToWorkspaceFromAllApps(final Launcher.State fromState, 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); } }; } }; startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage, appsView, appsView.getContentView(), appsView.getRevealView(), animated, onCompleteRunnable, cb); } /** * Starts and animation to the workspace from the widgets view. */ private void startAnimationToWorkspaceFromWidgets(final Launcher.State fromState, 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) { revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark)); } @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(), 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 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 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, 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 (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(); } // Animation complete callback pCb.onAnimationComplete(revealView, contentView, allAppsButtonView); // 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); } // This can hold unnecessary references to views. mStateAnimation = null; } }); 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); // 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; } } }