Home | History | Annotate | Download | only in launcher3
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.launcher3;
     18 
     19 import android.animation.Animator;
     20 import android.animation.AnimatorListenerAdapter;
     21 import android.animation.AnimatorSet;
     22 import android.animation.ObjectAnimator;
     23 import android.animation.TimeInterpolator;
     24 import android.animation.ValueAnimator;
     25 import android.content.Context;
     26 import android.content.res.Resources;
     27 import android.view.View;
     28 import android.view.ViewGroup;
     29 import android.view.accessibility.AccessibilityManager;
     30 import android.view.accessibility.AccessibilityNodeInfo;
     31 import android.view.animation.DecelerateInterpolator;
     32 
     33 import com.android.launcher3.util.Thunk;
     34 
     35 import java.util.HashMap;
     36 
     37 /**
     38  * A convenience class to update a view's visibility state after an alpha animation.
     39  */
     40 class AlphaUpdateListener extends AnimatorListenerAdapter implements ValueAnimator.AnimatorUpdateListener {
     41     private static final float ALPHA_CUTOFF_THRESHOLD = 0.01f;
     42 
     43     private View mView;
     44     private boolean mAccessibilityEnabled;
     45 
     46     public AlphaUpdateListener(View v, boolean accessibilityEnabled) {
     47         mView = v;
     48         mAccessibilityEnabled = accessibilityEnabled;
     49     }
     50 
     51     @Override
     52     public void onAnimationUpdate(ValueAnimator arg0) {
     53         updateVisibility(mView, mAccessibilityEnabled);
     54     }
     55 
     56     public static void updateVisibility(View view, boolean accessibilityEnabled) {
     57         // We want to avoid the extra layout pass by setting the views to GONE unless
     58         // accessibility is on, in which case not setting them to GONE causes a glitch.
     59         int invisibleState = accessibilityEnabled ? View.GONE : View.INVISIBLE;
     60         if (view.getAlpha() < ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != invisibleState) {
     61             view.setVisibility(invisibleState);
     62         } else if (view.getAlpha() > ALPHA_CUTOFF_THRESHOLD
     63                 && view.getVisibility() != View.VISIBLE) {
     64             view.setVisibility(View.VISIBLE);
     65         }
     66     }
     67 
     68     @Override
     69     public void onAnimationEnd(Animator arg0) {
     70         updateVisibility(mView, mAccessibilityEnabled);
     71     }
     72 
     73     @Override
     74     public void onAnimationStart(Animator arg0) {
     75         // We want the views to be visible for animation, so fade-in/out is visible
     76         mView.setVisibility(View.VISIBLE);
     77     }
     78 }
     79 
     80 /**
     81  * This interpolator emulates the rate at which the perceived scale of an object changes
     82  * as its distance from a camera increases. When this interpolator is applied to a scale
     83  * animation on a view, it evokes the sense that the object is shrinking due to moving away
     84  * from the camera.
     85  */
     86 class ZInterpolator implements TimeInterpolator {
     87     private float focalLength;
     88 
     89     public ZInterpolator(float foc) {
     90         focalLength = foc;
     91     }
     92 
     93     public float getInterpolation(float input) {
     94         return (1.0f - focalLength / (focalLength + input)) /
     95                 (1.0f - focalLength / (focalLength + 1.0f));
     96     }
     97 }
     98 
     99 /**
    100  * The exact reverse of ZInterpolator.
    101  */
    102 class InverseZInterpolator implements TimeInterpolator {
    103     private ZInterpolator zInterpolator;
    104     public InverseZInterpolator(float foc) {
    105         zInterpolator = new ZInterpolator(foc);
    106     }
    107     public float getInterpolation(float input) {
    108         return 1 - zInterpolator.getInterpolation(1 - input);
    109     }
    110 }
    111 
    112 /**
    113  * InverseZInterpolator compounded with an ease-out.
    114  */
    115 class ZoomInInterpolator implements TimeInterpolator {
    116     private final InverseZInterpolator inverseZInterpolator = new InverseZInterpolator(0.35f);
    117     private final DecelerateInterpolator decelerate = new DecelerateInterpolator(3.0f);
    118 
    119     public float getInterpolation(float input) {
    120         return decelerate.getInterpolation(inverseZInterpolator.getInterpolation(input));
    121     }
    122 }
    123 
    124 /**
    125  * Stores the transition states for convenience.
    126  */
    127 class TransitionStates {
    128 
    129     // Raw states
    130     final boolean oldStateIsNormal;
    131     final boolean oldStateIsSpringLoaded;
    132     final boolean oldStateIsNormalHidden;
    133     final boolean oldStateIsOverviewHidden;
    134     final boolean oldStateIsOverview;
    135 
    136     final boolean stateIsNormal;
    137     final boolean stateIsSpringLoaded;
    138     final boolean stateIsNormalHidden;
    139     final boolean stateIsOverviewHidden;
    140     final boolean stateIsOverview;
    141 
    142     // Convenience members
    143     final boolean workspaceToAllApps;
    144     final boolean overviewToAllApps;
    145     final boolean allAppsToWorkspace;
    146     final boolean workspaceToOverview;
    147     final boolean overviewToWorkspace;
    148 
    149     public TransitionStates(final Workspace.State fromState, final Workspace.State toState) {
    150         oldStateIsNormal = (fromState == Workspace.State.NORMAL);
    151         oldStateIsSpringLoaded = (fromState == Workspace.State.SPRING_LOADED);
    152         oldStateIsNormalHidden = (fromState == Workspace.State.NORMAL_HIDDEN);
    153         oldStateIsOverviewHidden = (fromState == Workspace.State.OVERVIEW_HIDDEN);
    154         oldStateIsOverview = (fromState == Workspace.State.OVERVIEW);
    155 
    156         stateIsNormal = (toState == Workspace.State.NORMAL);
    157         stateIsSpringLoaded = (toState == Workspace.State.SPRING_LOADED);
    158         stateIsNormalHidden = (toState == Workspace.State.NORMAL_HIDDEN);
    159         stateIsOverviewHidden = (toState == Workspace.State.OVERVIEW_HIDDEN);
    160         stateIsOverview = (toState == Workspace.State.OVERVIEW);
    161 
    162         workspaceToOverview = (oldStateIsNormal && stateIsOverview);
    163         workspaceToAllApps = (oldStateIsNormal && stateIsNormalHidden);
    164         overviewToWorkspace = (oldStateIsOverview && stateIsNormal);
    165         overviewToAllApps = (oldStateIsOverview && stateIsOverviewHidden);
    166         allAppsToWorkspace = (stateIsNormalHidden && stateIsNormal);
    167     }
    168 }
    169 
    170 /**
    171  * Manages the animations between each of the workspace states.
    172  */
    173 public class WorkspaceStateTransitionAnimation {
    174 
    175     public static final String TAG = "WorkspaceStateTransitionAnimation";
    176 
    177     public static final int SCROLL_TO_CURRENT_PAGE = -1;
    178     @Thunk static final int BACKGROUND_FADE_OUT_DURATION = 350;
    179 
    180     final @Thunk Launcher mLauncher;
    181     final @Thunk Workspace mWorkspace;
    182 
    183     @Thunk AnimatorSet mStateAnimator;
    184     @Thunk float[] mOldBackgroundAlphas;
    185     @Thunk float[] mOldAlphas;
    186     @Thunk float[] mNewBackgroundAlphas;
    187     @Thunk float[] mNewAlphas;
    188     @Thunk int mLastChildCount = -1;
    189 
    190     @Thunk float mCurrentScale;
    191     @Thunk float mNewScale;
    192 
    193     @Thunk final ZoomInInterpolator mZoomInInterpolator = new ZoomInInterpolator();
    194 
    195     @Thunk float mSpringLoadedShrinkFactor;
    196     @Thunk float mOverviewModeShrinkFactor;
    197     @Thunk float mWorkspaceScrimAlpha;
    198     @Thunk int mAllAppsTransitionTime;
    199     @Thunk int mOverviewTransitionTime;
    200     @Thunk int mOverlayTransitionTime;
    201     @Thunk boolean mWorkspaceFadeInAdjacentScreens;
    202 
    203     public WorkspaceStateTransitionAnimation(Launcher launcher, Workspace workspace) {
    204         mLauncher = launcher;
    205         mWorkspace = workspace;
    206 
    207         DeviceProfile grid = mLauncher.getDeviceProfile();
    208         Resources res = launcher.getResources();
    209         mAllAppsTransitionTime = res.getInteger(R.integer.config_allAppsTransitionTime);
    210         mOverviewTransitionTime = res.getInteger(R.integer.config_overviewTransitionTime);
    211         mOverlayTransitionTime = res.getInteger(R.integer.config_overlayTransitionTime);
    212         mSpringLoadedShrinkFactor =
    213                 res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100f;
    214         mWorkspaceScrimAlpha = res.getInteger(R.integer.config_workspaceScrimAlpha) / 100f;
    215         mOverviewModeShrinkFactor = grid.getOverviewModeScale(Utilities.isRtl(res));
    216         mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens();
    217     }
    218 
    219     public AnimatorSet getAnimationToState(Workspace.State fromState, Workspace.State toState,
    220             int toPage, boolean animated, boolean hasOverlaySearchBar,
    221             HashMap<View, Integer> layerViews) {
    222         AccessibilityManager am = (AccessibilityManager)
    223                 mLauncher.getSystemService(Context.ACCESSIBILITY_SERVICE);
    224         final boolean accessibilityEnabled = am.isEnabled();
    225         TransitionStates states = new TransitionStates(fromState, toState);
    226         int duration = getAnimationDuration(states);
    227         animateWorkspace(states, toPage, animated, duration, layerViews,
    228                 accessibilityEnabled);
    229         animateSearchBar(states, animated, duration, hasOverlaySearchBar, layerViews,
    230                 accessibilityEnabled);
    231         animateBackgroundGradient(states, animated, BACKGROUND_FADE_OUT_DURATION);
    232         return mStateAnimator;
    233     }
    234 
    235     public float getFinalScale() {
    236         return mNewScale;
    237     }
    238 
    239     /**
    240      * Reinitializes the arrays that we need for the animations on each page.
    241      */
    242     private void reinitializeAnimationArrays() {
    243         final int childCount = mWorkspace.getChildCount();
    244         if (mLastChildCount == childCount) return;
    245 
    246         mOldBackgroundAlphas = new float[childCount];
    247         mOldAlphas = new float[childCount];
    248         mNewBackgroundAlphas = new float[childCount];
    249         mNewAlphas = new float[childCount];
    250     }
    251 
    252     /**
    253      * Returns the proper animation duration for a transition.
    254      */
    255     private int getAnimationDuration(TransitionStates states) {
    256         if (states.workspaceToAllApps || states.overviewToAllApps) {
    257             return mAllAppsTransitionTime;
    258         } else if (states.workspaceToOverview || states.overviewToWorkspace) {
    259             return mOverviewTransitionTime;
    260         } else {
    261             return mOverlayTransitionTime;
    262         }
    263     }
    264 
    265     /**
    266      * Starts a transition animation for the workspace.
    267      */
    268     private void animateWorkspace(final TransitionStates states, int toPage, final boolean animated,
    269                                   final int duration, final HashMap<View, Integer> layerViews,
    270                                   final boolean accessibilityEnabled) {
    271         // Reinitialize animation arrays for the current workspace state
    272         reinitializeAnimationArrays();
    273 
    274         // Cancel existing workspace animations and create a new animator set if requested
    275         cancelAnimation();
    276         if (animated) {
    277             mStateAnimator = LauncherAnimUtils.createAnimatorSet();
    278         }
    279 
    280         // Update the workspace state
    281         float finalBackgroundAlpha = (states.stateIsSpringLoaded || states.stateIsOverview) ?
    282                 1.0f : 0f;
    283         float finalHotseatAndPageIndicatorAlpha = (states.stateIsNormal || states.stateIsSpringLoaded) ?
    284                 1f : 0f;
    285         float finalOverviewPanelAlpha = states.stateIsOverview ? 1f : 0f;
    286         float finalWorkspaceTranslationY = states.stateIsOverview || states.stateIsOverviewHidden ?
    287                 mWorkspace.getOverviewModeTranslationY() : 0;
    288 
    289         final int childCount = mWorkspace.getChildCount();
    290         final int customPageCount = mWorkspace.numCustomPages();
    291 
    292         mNewScale = 1.0f;
    293 
    294         if (states.oldStateIsOverview) {
    295             mWorkspace.disableFreeScroll();
    296         } else if (states.stateIsOverview) {
    297             mWorkspace.enableFreeScroll();
    298         }
    299 
    300         if (!states.stateIsNormal) {
    301             if (states.stateIsSpringLoaded) {
    302                 mNewScale = mSpringLoadedShrinkFactor;
    303             } else if (states.stateIsOverview || states.stateIsOverviewHidden) {
    304                 mNewScale = mOverviewModeShrinkFactor;
    305             }
    306         }
    307 
    308         if (toPage == SCROLL_TO_CURRENT_PAGE) {
    309             toPage = mWorkspace.getPageNearestToCenterOfScreen();
    310         }
    311         mWorkspace.snapToPage(toPage, duration, mZoomInInterpolator);
    312 
    313         for (int i = 0; i < childCount; i++) {
    314             final CellLayout cl = (CellLayout) mWorkspace.getChildAt(i);
    315             boolean isCurrentPage = (i == toPage);
    316             float initialAlpha = cl.getShortcutsAndWidgets().getAlpha();
    317             float finalAlpha;
    318             if (states.stateIsNormalHidden || states.stateIsOverviewHidden) {
    319                 finalAlpha = 0f;
    320             } else if (states.stateIsNormal && mWorkspaceFadeInAdjacentScreens) {
    321                 finalAlpha = (i == toPage || i < customPageCount) ? 1f : 0f;
    322             } else {
    323                 finalAlpha = 1f;
    324             }
    325 
    326             // If we are animating to/from the small state, then hide the side pages and fade the
    327             // current page in
    328             if (!mWorkspace.isSwitchingState()) {
    329                 if (states.workspaceToAllApps || states.allAppsToWorkspace) {
    330                     if (states.allAppsToWorkspace && isCurrentPage) {
    331                         initialAlpha = 0f;
    332                     } else if (!isCurrentPage) {
    333                         initialAlpha = finalAlpha = 0f;
    334                     }
    335                     cl.setShortcutAndWidgetAlpha(initialAlpha);
    336                 }
    337             }
    338 
    339             mOldAlphas[i] = initialAlpha;
    340             mNewAlphas[i] = finalAlpha;
    341             if (animated) {
    342                 mOldBackgroundAlphas[i] = cl.getBackgroundAlpha();
    343                 mNewBackgroundAlphas[i] = finalBackgroundAlpha;
    344             } else {
    345                 cl.setBackgroundAlpha(finalBackgroundAlpha);
    346                 cl.setShortcutAndWidgetAlpha(finalAlpha);
    347             }
    348         }
    349 
    350         final ViewGroup overviewPanel = mLauncher.getOverviewPanel();
    351         final View hotseat = mLauncher.getHotseat();
    352         final View pageIndicator = mWorkspace.getPageIndicator();
    353         if (animated) {
    354             LauncherViewPropertyAnimator scale = new LauncherViewPropertyAnimator(mWorkspace);
    355             scale.scaleX(mNewScale)
    356                     .scaleY(mNewScale)
    357                     .translationY(finalWorkspaceTranslationY)
    358                     .setDuration(duration)
    359                     .setInterpolator(mZoomInInterpolator);
    360             mStateAnimator.play(scale);
    361             for (int index = 0; index < childCount; index++) {
    362                 final int i = index;
    363                 final CellLayout cl = (CellLayout) mWorkspace.getChildAt(i);
    364                 float currentAlpha = cl.getShortcutsAndWidgets().getAlpha();
    365                 if (mOldAlphas[i] == 0 && mNewAlphas[i] == 0) {
    366                     cl.setBackgroundAlpha(mNewBackgroundAlphas[i]);
    367                     cl.setShortcutAndWidgetAlpha(mNewAlphas[i]);
    368                 } else {
    369                     if (layerViews != null) {
    370                         layerViews.put(cl, LauncherStateTransitionAnimation.BUILD_LAYER);
    371                     }
    372                     if (mOldAlphas[i] != mNewAlphas[i] || currentAlpha != mNewAlphas[i]) {
    373                         LauncherViewPropertyAnimator alphaAnim =
    374                                 new LauncherViewPropertyAnimator(cl.getShortcutsAndWidgets());
    375                         alphaAnim.alpha(mNewAlphas[i])
    376                                 .setDuration(duration)
    377                                 .setInterpolator(mZoomInInterpolator);
    378                         mStateAnimator.play(alphaAnim);
    379                     }
    380                     if (mOldBackgroundAlphas[i] != 0 ||
    381                             mNewBackgroundAlphas[i] != 0) {
    382                         ValueAnimator bgAnim = ObjectAnimator.ofFloat(cl, "backgroundAlpha",
    383                                 mOldBackgroundAlphas[i], mNewBackgroundAlphas[i]);
    384                                 LauncherAnimUtils.ofFloat(cl, 0f, 1f);
    385                         bgAnim.setInterpolator(mZoomInInterpolator);
    386                         bgAnim.setDuration(duration);
    387                         mStateAnimator.play(bgAnim);
    388                     }
    389                 }
    390             }
    391             Animator pageIndicatorAlpha;
    392             if (pageIndicator != null) {
    393                 pageIndicatorAlpha = new LauncherViewPropertyAnimator(pageIndicator)
    394                         .alpha(finalHotseatAndPageIndicatorAlpha).withLayer();
    395                 pageIndicatorAlpha.addListener(new AlphaUpdateListener(pageIndicator,
    396                         accessibilityEnabled));
    397             } else {
    398                 // create a dummy animation so we don't need to do null checks later
    399                 pageIndicatorAlpha = ValueAnimator.ofFloat(0, 0);
    400             }
    401 
    402             LauncherViewPropertyAnimator hotseatAlpha = new LauncherViewPropertyAnimator(hotseat)
    403                     .alpha(finalHotseatAndPageIndicatorAlpha);
    404             hotseatAlpha.addListener(new AlphaUpdateListener(hotseat, accessibilityEnabled));
    405 
    406             LauncherViewPropertyAnimator overviewPanelAlpha =
    407                     new LauncherViewPropertyAnimator(overviewPanel).alpha(finalOverviewPanelAlpha);
    408             overviewPanelAlpha.addListener(new AlphaUpdateListener(overviewPanel,
    409                     accessibilityEnabled));
    410 
    411             // For animation optimations, we may need to provide the Launcher transition
    412             // with a set of views on which to force build layers in certain scenarios.
    413             hotseat.setLayerType(View.LAYER_TYPE_HARDWARE, null);
    414             overviewPanel.setLayerType(View.LAYER_TYPE_HARDWARE, null);
    415             if (layerViews != null) {
    416                 // If layerViews is not null, we add these views, and indicate that
    417                 // the caller can manage layer state.
    418                 layerViews.put(hotseat, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER);
    419                 layerViews.put(overviewPanel, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER);
    420             } else {
    421                 // Otherwise let the animator handle layer management.
    422                 hotseatAlpha.withLayer();
    423                 overviewPanelAlpha.withLayer();
    424             }
    425 
    426             if (states.workspaceToOverview) {
    427                 pageIndicatorAlpha.setInterpolator(new DecelerateInterpolator(2));
    428                 hotseatAlpha.setInterpolator(new DecelerateInterpolator(2));
    429                 overviewPanelAlpha.setInterpolator(null);
    430             } else if (states.overviewToWorkspace) {
    431                 pageIndicatorAlpha.setInterpolator(null);
    432                 hotseatAlpha.setInterpolator(null);
    433                 overviewPanelAlpha.setInterpolator(new DecelerateInterpolator(2));
    434             }
    435 
    436             overviewPanelAlpha.setDuration(duration);
    437             pageIndicatorAlpha.setDuration(duration);
    438             hotseatAlpha.setDuration(duration);
    439 
    440             mStateAnimator.play(overviewPanelAlpha);
    441             mStateAnimator.play(hotseatAlpha);
    442             mStateAnimator.play(pageIndicatorAlpha);
    443             mStateAnimator.addListener(new AnimatorListenerAdapter() {
    444                 @Override
    445                 public void onAnimationEnd(Animator animation) {
    446                     mStateAnimator = null;
    447 
    448                     if (accessibilityEnabled && overviewPanel.getVisibility() == View.VISIBLE) {
    449                         overviewPanel.getChildAt(0).performAccessibilityAction(
    450                                 AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
    451                     }
    452                 }
    453             });
    454         } else {
    455             overviewPanel.setAlpha(finalOverviewPanelAlpha);
    456             AlphaUpdateListener.updateVisibility(overviewPanel, accessibilityEnabled);
    457             hotseat.setAlpha(finalHotseatAndPageIndicatorAlpha);
    458             AlphaUpdateListener.updateVisibility(hotseat, accessibilityEnabled);
    459             if (pageIndicator != null) {
    460                 pageIndicator.setAlpha(finalHotseatAndPageIndicatorAlpha);
    461                 AlphaUpdateListener.updateVisibility(pageIndicator, accessibilityEnabled);
    462             }
    463             mWorkspace.updateCustomContentVisibility();
    464             mWorkspace.setScaleX(mNewScale);
    465             mWorkspace.setScaleY(mNewScale);
    466             mWorkspace.setTranslationY(finalWorkspaceTranslationY);
    467 
    468             if (accessibilityEnabled && overviewPanel.getVisibility() == View.VISIBLE) {
    469                 overviewPanel.getChildAt(0).performAccessibilityAction(
    470                         AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
    471             }
    472         }
    473     }
    474 
    475     /**
    476      * Coordinates with the workspace animation to animate the search bar.
    477      *
    478      * TODO: This should really be coordinated with the SearchDropTargetBar, otherwise the
    479      *       bar has no idea that it is hidden, and this has no idea what state the bar is
    480      *       actually in.
    481      */
    482     private void animateSearchBar(TransitionStates states, boolean animated, int duration,
    483             boolean hasOverlaySearchBar, final HashMap<View, Integer> layerViews,
    484             final boolean accessibilityEnabled) {
    485 
    486         // The search bar is only visible in the workspace
    487         final View searchBar = mLauncher.getOrCreateQsbBar();
    488         if (searchBar != null) {
    489             final boolean searchBarWillBeShown = states.stateIsNormal;
    490             final float finalSearchBarAlpha = searchBarWillBeShown ? 1f : 0f;
    491             if (animated) {
    492                 if (hasOverlaySearchBar) {
    493                     // If there is an overlay search bar, then we will coordinate with it.
    494                     mStateAnimator.addListener(new AnimatorListenerAdapter() {
    495                         @Override
    496                         public void onAnimationStart(Animator animation) {
    497                             // If we are transitioning to a visible search bar, show it immediately
    498                             // and let the overlay search bar has faded out
    499                             if (searchBarWillBeShown) {
    500                                 searchBar.setAlpha(finalSearchBarAlpha);
    501                                 AlphaUpdateListener.updateVisibility(searchBar, accessibilityEnabled);
    502                             }
    503                         }
    504 
    505                         @Override
    506                         public void onAnimationEnd(Animator animation) {
    507                             // If we are transitioning to a hidden search bar, hide it only after
    508                             // the overlay search bar has faded in
    509                             if (!searchBarWillBeShown) {
    510                                 searchBar.setAlpha(finalSearchBarAlpha);
    511                                 AlphaUpdateListener.updateVisibility(searchBar, accessibilityEnabled);
    512                             }
    513                         }
    514                     });
    515                 } else {
    516                     // Otherwise, we can just do the normal animation
    517                     LauncherViewPropertyAnimator searchBarAlpha =
    518                             new LauncherViewPropertyAnimator(searchBar).alpha(finalSearchBarAlpha);
    519                     searchBarAlpha.addListener(new AlphaUpdateListener(searchBar,
    520                             accessibilityEnabled));
    521                     searchBar.setLayerType(View.LAYER_TYPE_HARDWARE, null);
    522                     if (layerViews != null) {
    523                         // If layerViews is not null, we add these views, and indicate that
    524                         // the caller can manage layer state.
    525                         layerViews.put(searchBar, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER);
    526                     } else {
    527                         // Otherwise let the animator handle layer management.
    528                         searchBarAlpha.withLayer();
    529                     }
    530                     searchBarAlpha.setDuration(duration);
    531                     mStateAnimator.play(searchBarAlpha);
    532                 }
    533             } else {
    534                 // Set the search bar state immediately
    535                 searchBar.setAlpha(finalSearchBarAlpha);
    536                 AlphaUpdateListener.updateVisibility(searchBar, accessibilityEnabled);
    537             }
    538         }
    539     }
    540 
    541     /**
    542      * Animates the background scrim. Add to the state animator to prevent jankiness.
    543      *
    544      * @param finalAlpha the final alpha for the background scrim
    545      * @param animated whether or not to set the background alpha immediately
    546      * @duration duration of the animation
    547      */
    548     private void animateBackgroundGradient(TransitionStates states,
    549             boolean animated, int duration) {
    550 
    551         final DragLayer dragLayer = mLauncher.getDragLayer();
    552         final float startAlpha = dragLayer.getBackgroundAlpha();
    553         float finalAlpha = states.stateIsNormal ? 0 : mWorkspaceScrimAlpha;
    554 
    555         if (finalAlpha != startAlpha) {
    556             if (animated) {
    557                 // These properties refer to the background protection gradient used for AllApps
    558                 // and Widget tray.
    559                 ValueAnimator bgFadeOutAnimation =
    560                         LauncherAnimUtils.ofFloat(mWorkspace, startAlpha, finalAlpha);
    561                 bgFadeOutAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    562                     @Override
    563                     public void onAnimationUpdate(ValueAnimator animation) {
    564                         dragLayer.setBackgroundAlpha(
    565                                 ((Float)animation.getAnimatedValue()).floatValue());
    566                     }
    567                 });
    568                 bgFadeOutAnimation.setInterpolator(new DecelerateInterpolator(1.5f));
    569                 bgFadeOutAnimation.setDuration(duration);
    570                 mStateAnimator.play(bgFadeOutAnimation);
    571             } else {
    572                 dragLayer.setBackgroundAlpha(finalAlpha);
    573             }
    574         }
    575     }
    576 
    577     /**
    578      * Cancels the current animation.
    579      */
    580     private void cancelAnimation() {
    581         if (mStateAnimator != null) {
    582             mStateAnimator.setDuration(0);
    583             mStateAnimator.cancel();
    584         }
    585         mStateAnimator = null;
    586     }
    587 }