| /* |
| * 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.content.res.Resources; |
| import android.util.Log; |
| import android.view.View; |
| import android.view.ViewAnimationUtils; |
| import android.view.animation.AccelerateInterpolator; |
| import android.view.animation.DecelerateInterpolator; |
| |
| 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; |
| |
| private Launcher mLauncher; |
| private Callbacks mCb; |
| private 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 AppsContainerView 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 = LauncherAppState.getInstance(). |
| getDynamicGrid().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(), null, animated, cb); |
| } |
| |
| /** |
| * Starts an animation to the widgets view. |
| */ |
| public void startAnimationToWidgets(final boolean animated) { |
| final AppsCustomizeTabHost toView = mLauncher.getWidgetsView(); |
| PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { |
| @Override |
| public void onRevealViewVisible(View revealView, View contentView, |
| View allAppsButtonView) { |
| // Hide the real page background, and swap in the fake one |
| ((AppsCustomizePagedView) contentView).setPageBackgroundsVisible(false); |
| revealView.setBackground(mLauncher.getDrawable(R.drawable.quantum_panel_dark)); |
| } |
| @Override |
| public void onAnimationComplete(View revealView, View contentView, View allAppsButtonView) { |
| // Show the real page background |
| ((AppsCustomizePagedView) contentView).setPageBackgroundsVisible(true); |
| } |
| @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(), toView.getPageIndicators(), 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 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, animated, |
| onCompleteRunnable); |
| } else { |
| startAnimationToWorkspaceFromWidgets(fromState, toWorkspaceState, animated, |
| onCompleteRunnable); |
| } |
| } |
| |
| /** |
| * Creates and starts a new animation to a particular overlay view. |
| */ |
| private void startAnimationToOverlay(final Workspace.State toWorkspaceState, final View toView, |
| final View contentView, final View revealView, final View pageIndicatorsView, |
| final boolean animated, final PrivateTransitionCallbacks pCb) { |
| final Resources res = mLauncher.getResources(); |
| final boolean material = Utilities.isLmpOrAbove(); |
| final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime); |
| final int itemsAlphaStagger = |
| res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger); |
| |
| 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.getWorkspace().getChangeStateAnimation( |
| toWorkspaceState, 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.sqrt((width * width) / 4 + (height * height) / 4); |
| 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 page indicators |
| if (pageIndicatorsView != null) { |
| pageIndicatorsView.setAlpha(0.01f); |
| ObjectAnimator indicatorsAlpha = |
| ObjectAnimator.ofFloat(pageIndicatorsView, "alpha", 1f); |
| indicatorsAlpha.setDuration(revealDuration); |
| mStateAnimation.play(indicatorsAlpha); |
| } |
| |
| // 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 = ViewAnimationUtils.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); |
| } |
| } |
| |
| // Hide the search bar |
| 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 |
| for (View v : layerViews.keySet()) { |
| if (layerViews.get(v) == BUILD_AND_SET_LAYER) { |
| v.setLayerType(View.LAYER_TYPE_HARDWARE, null); |
| } |
| if (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); |
| |
| // Hide the search bar |
| 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 boolean animated, |
| final Runnable onCompleteRunnable) { |
| AppsContainerView 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 = LauncherAppState.getInstance(). |
| getDynamicGrid().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, appsView, appsView.getContentView(), |
| appsView.getRevealView(), null /* pageIndicatorsView */, 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 boolean animated, |
| final Runnable onCompleteRunnable) { |
| AppsCustomizeTabHost widgetsView = mLauncher.getWidgetsView(); |
| PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { |
| @Override |
| public void onRevealViewVisible(View revealView, View contentView, View allAppsButtonView) { |
| AppsCustomizePagedView pagedView = ((AppsCustomizePagedView) contentView); |
| |
| // Hide the real page background, and swap in the fake one |
| pagedView.stopScrolling(); |
| pagedView.setPageBackgroundsVisible(false); |
| revealView.setBackground(mLauncher.getDrawable(R.drawable.quantum_panel_dark)); |
| |
| // Hide the side pages of the Widget tray to avoid some ugly edge cases |
| final View currentPage = pagedView.getPageAt(pagedView.getNextPage()); |
| int count = pagedView.getChildCount(); |
| for (int i = 0; i < count; i++) { |
| View child = pagedView.getChildAt(i); |
| if (child != currentPage) { |
| child.setVisibility(View.INVISIBLE); |
| } |
| } |
| } |
| @Override |
| public void onAnimationComplete(View revealView, View contentView, View allAppsButtonView) { |
| AppsCustomizePagedView pagedView = ((AppsCustomizePagedView) contentView); |
| |
| // Show the real page background and force-update the page |
| pagedView.setPageBackgroundsVisible(true); |
| pagedView.setCurrentPage(pagedView.getNextPage()); |
| pagedView.updateCurrentPageScroll(); |
| |
| // Unhide the side pages |
| int count = pagedView.getChildCount(); |
| for (int i = 0; i < count; i++) { |
| View child = pagedView.getChildAt(i); |
| child.setVisibility(View.VISIBLE); |
| } |
| } |
| @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, widgetsView, |
| widgetsView.getContentView(), widgetsView.getRevealView(), |
| widgetsView.getPageIndicators(), animated, onCompleteRunnable, cb); |
| } |
| |
| /** |
| * Creates and starts a new animation to the workspace. |
| */ |
| private void startAnimationToWorkspaceFromOverlay(final Workspace.State toWorkspaceState, |
| final View fromView, final View contentView, final View revealView, |
| final View pageIndicatorsView, 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_appsCustomizeRevealTime); |
| final int itemsAlphaStagger = |
| res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger); |
| |
| 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.getWorkspace().getChangeStateAnimation( |
| toWorkspaceState, 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.sqrt((width * width) / 4 + (height * height) / 4); |
| 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 = LauncherAnimUtils.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 = LauncherAnimUtils.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 = LauncherAnimUtils.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 = LauncherAnimUtils.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 = LauncherAnimUtils.ofFloat(contentView, "alpha", 1f, 0f); |
| itemsAlpha.setDuration(100); |
| itemsAlpha.setInterpolator(decelerateInterpolator); |
| mStateAnimation.play(itemsAlpha); |
| |
| // Setup the page indicators animation |
| if (pageIndicatorsView != null) { |
| pageIndicatorsView.setAlpha(1f); |
| ObjectAnimator indicatorsAlpha = |
| LauncherAnimUtils.ofFloat(pageIndicatorsView, "alpha", 0f); |
| indicatorsAlpha.setDuration(revealDuration); |
| indicatorsAlpha.setInterpolator(new DecelerateInterpolator(1.5f)); |
| mStateAnimation.play(indicatorsAlpha); |
| } |
| |
| if (material) { |
| // Animate the all apps button |
| float finalRadius = pCb.getMaterialRevealViewStartFinalRadius(); |
| AnimatorListenerAdapter listener = |
| pCb.getMaterialRevealViewAnimatorListener(revealView, allAppsButtonView); |
| Animator reveal = |
| LauncherAnimUtils.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 |
| for (View v : layerViews.keySet()) { |
| if (layerViews.get(v) == BUILD_AND_SET_LAYER) { |
| v.setLayerType(View.LAYER_TYPE_HARDWARE, null); |
| } |
| if (Utilities.isLmpOrAbove()) { |
| 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; |
| } |
| } |
| } |