Home | History | Annotate | Download | only in phone
      1 /*
      2  * Copyright (C) 2016 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.systemui.statusbar.phone;
     18 
     19 import android.content.Context;
     20 import android.content.res.Configuration;
     21 import android.graphics.Canvas;
     22 import android.graphics.Color;
     23 import android.graphics.Paint;
     24 import android.graphics.drawable.Icon;
     25 import android.support.v4.util.ArrayMap;
     26 import android.support.v4.util.ArraySet;
     27 import android.util.AttributeSet;
     28 import android.view.View;
     29 
     30 import com.android.internal.statusbar.StatusBarIcon;
     31 import com.android.systemui.Interpolators;
     32 import com.android.systemui.R;
     33 import com.android.systemui.statusbar.AlphaOptimizedFrameLayout;
     34 import com.android.systemui.statusbar.StatusBarIconView;
     35 import com.android.systemui.statusbar.stack.AnimationFilter;
     36 import com.android.systemui.statusbar.stack.AnimationProperties;
     37 import com.android.systemui.statusbar.stack.StackStateAnimator;
     38 import com.android.systemui.statusbar.stack.ViewState;
     39 
     40 import java.util.ArrayList;
     41 import java.util.HashMap;
     42 
     43 /**
     44  * A container for notification icons. It handles overflowing icons properly and positions them
     45  * correctly on the screen.
     46  */
     47 public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
     48     /**
     49      * A float value indicating how much before the overflow start the icons should transform into
     50      * a dot. A value of 0 means that they are exactly at the end and a value of 1 means it starts
     51      * 1 icon width early.
     52      */
     53     public static final float OVERFLOW_EARLY_AMOUNT = 0.2f;
     54     private static final int NO_VALUE = Integer.MIN_VALUE;
     55     private static final String TAG = "NotificationIconContainer";
     56     private static final boolean DEBUG = false;
     57     private static final int CANNED_ANIMATION_DURATION = 100;
     58     private static final AnimationProperties DOT_ANIMATION_PROPERTIES = new AnimationProperties() {
     59         private AnimationFilter mAnimationFilter = new AnimationFilter().animateX();
     60 
     61         @Override
     62         public AnimationFilter getAnimationFilter() {
     63             return mAnimationFilter;
     64         }
     65     }.setDuration(200);
     66 
     67     private static final AnimationProperties ICON_ANIMATION_PROPERTIES = new AnimationProperties() {
     68         private AnimationFilter mAnimationFilter = new AnimationFilter().animateY().animateAlpha()
     69                 .animateScale();
     70 
     71         @Override
     72         public AnimationFilter getAnimationFilter() {
     73             return mAnimationFilter;
     74         }
     75 
     76     }.setDuration(CANNED_ANIMATION_DURATION)
     77             .setCustomInterpolator(View.TRANSLATION_Y, Interpolators.ICON_OVERSHOT);
     78 
     79     private static final AnimationProperties mTempProperties = new AnimationProperties() {
     80         private AnimationFilter mAnimationFilter = new AnimationFilter();
     81 
     82         @Override
     83         public AnimationFilter getAnimationFilter() {
     84             return mAnimationFilter;
     85         }
     86     };
     87 
     88     private static final AnimationProperties ADD_ICON_PROPERTIES = new AnimationProperties() {
     89         private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha();
     90 
     91         @Override
     92         public AnimationFilter getAnimationFilter() {
     93             return mAnimationFilter;
     94         }
     95     }.setDuration(200).setDelay(50);
     96 
     97     private static final AnimationProperties UNDARK_PROPERTIES = new AnimationProperties() {
     98         private AnimationFilter mAnimationFilter = new AnimationFilter()
     99                 .animateX();
    100 
    101         @Override
    102         public AnimationFilter getAnimationFilter() {
    103             return mAnimationFilter;
    104         }
    105     }.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP);
    106     public static final int MAX_VISIBLE_ICONS_WHEN_DARK = 5;
    107 
    108     private boolean mShowAllIcons = true;
    109     private final HashMap<View, IconState> mIconStates = new HashMap<>();
    110     private int mDotPadding;
    111     private int mStaticDotRadius;
    112     private int mActualLayoutWidth = NO_VALUE;
    113     private float mActualPaddingEnd = NO_VALUE;
    114     private float mActualPaddingStart = NO_VALUE;
    115     private boolean mDark;
    116     private boolean mChangingViewPositions;
    117     private int mAddAnimationStartIndex = -1;
    118     private int mCannedAnimationStartIndex = -1;
    119     private int mSpeedBumpIndex = -1;
    120     private int mIconSize;
    121     private float mOpenedAmount = 0.0f;
    122     private float mVisualOverflowAdaption;
    123     private boolean mDisallowNextAnimation;
    124     private boolean mAnimationsEnabled = true;
    125     private ArrayMap<String, ArrayList<StatusBarIcon>> mReplacingIcons;
    126 
    127     public NotificationIconContainer(Context context, AttributeSet attrs) {
    128         super(context, attrs);
    129         initDimens();
    130         setWillNotDraw(!DEBUG);
    131     }
    132 
    133     private void initDimens() {
    134         mDotPadding = getResources().getDimensionPixelSize(R.dimen.overflow_icon_dot_padding);
    135         mStaticDotRadius = getResources().getDimensionPixelSize(R.dimen.overflow_dot_radius);
    136     }
    137 
    138     @Override
    139     protected void onDraw(Canvas canvas) {
    140         super.onDraw(canvas);
    141         Paint paint = new Paint();
    142         paint.setColor(Color.RED);
    143         paint.setStyle(Paint.Style.STROKE);
    144         canvas.drawRect(getActualPaddingStart(), 0, getLayoutEnd(), getHeight(), paint);
    145     }
    146 
    147     @Override
    148     protected void onConfigurationChanged(Configuration newConfig) {
    149         super.onConfigurationChanged(newConfig);
    150         initDimens();
    151     }
    152     @Override
    153     protected void onLayout(boolean changed, int l, int t, int r, int b) {
    154         float centerY = getHeight() / 2.0f;
    155         // we layout all our children on the left at the top
    156         mIconSize = 0;
    157         for (int i = 0; i < getChildCount(); i++) {
    158             View child = getChildAt(i);
    159             // We need to layout all children even the GONE ones, such that the heights are
    160             // calculated correctly as they are used to calculate how many we can fit on the screen
    161             int width = child.getMeasuredWidth();
    162             int height = child.getMeasuredHeight();
    163             int top = (int) (centerY - height / 2.0f);
    164             child.layout(0, top, width, top + height);
    165             if (i == 0) {
    166                 mIconSize = child.getWidth();
    167             }
    168         }
    169         if (mShowAllIcons) {
    170             resetViewStates();
    171             calculateIconTranslations();
    172             applyIconStates();
    173         }
    174     }
    175 
    176     public void applyIconStates() {
    177         for (int i = 0; i < getChildCount(); i++) {
    178             View child = getChildAt(i);
    179             ViewState childState = mIconStates.get(child);
    180             if (childState != null) {
    181                 childState.applyToView(child);
    182             }
    183         }
    184         mAddAnimationStartIndex = -1;
    185         mCannedAnimationStartIndex = -1;
    186         mDisallowNextAnimation = false;
    187     }
    188 
    189     @Override
    190     public void onViewAdded(View child) {
    191         super.onViewAdded(child);
    192         boolean isReplacingIcon = isReplacingIcon(child);
    193         if (!mChangingViewPositions) {
    194             IconState v = new IconState();
    195             if (isReplacingIcon) {
    196                 v.justAdded = false;
    197                 v.justReplaced = true;
    198             }
    199             mIconStates.put(child, v);
    200         }
    201         int childIndex = indexOfChild(child);
    202         if (childIndex < getChildCount() - 1 && !isReplacingIcon
    203             && mIconStates.get(getChildAt(childIndex + 1)).iconAppearAmount > 0.0f) {
    204             if (mAddAnimationStartIndex < 0) {
    205                 mAddAnimationStartIndex = childIndex;
    206             } else {
    207                 mAddAnimationStartIndex = Math.min(mAddAnimationStartIndex, childIndex);
    208             }
    209         }
    210         if (mDark && child instanceof StatusBarIconView) {
    211             ((StatusBarIconView) child).setDark(mDark, false, 0);
    212         }
    213     }
    214 
    215     private boolean isReplacingIcon(View child) {
    216         if (mReplacingIcons == null) {
    217             return false;
    218         }
    219         if (!(child instanceof StatusBarIconView)) {
    220             return false;
    221         }
    222         StatusBarIconView iconView = (StatusBarIconView) child;
    223         Icon sourceIcon = iconView.getSourceIcon();
    224         String groupKey = iconView.getNotification().getGroupKey();
    225         ArrayList<StatusBarIcon> statusBarIcons = mReplacingIcons.get(groupKey);
    226         if (statusBarIcons != null) {
    227             StatusBarIcon replacedIcon = statusBarIcons.get(0);
    228             if (sourceIcon.sameAs(replacedIcon.icon)) {
    229                 return true;
    230             }
    231         }
    232         return false;
    233     }
    234 
    235     @Override
    236     public void onViewRemoved(View child) {
    237         super.onViewRemoved(child);
    238         if (child instanceof StatusBarIconView) {
    239             boolean isReplacingIcon = isReplacingIcon(child);
    240             final StatusBarIconView icon = (StatusBarIconView) child;
    241             if (icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN
    242                     && child.getVisibility() == VISIBLE && isReplacingIcon) {
    243                 int animationStartIndex = findFirstViewIndexAfter(icon.getTranslationX());
    244                 if (mAddAnimationStartIndex < 0) {
    245                     mAddAnimationStartIndex = animationStartIndex;
    246                 } else {
    247                     mAddAnimationStartIndex = Math.min(mAddAnimationStartIndex, animationStartIndex);
    248                 }
    249             }
    250             if (!mChangingViewPositions) {
    251                 mIconStates.remove(child);
    252                 if (!isReplacingIcon) {
    253                     addTransientView(icon, 0);
    254                     icon.setVisibleState(StatusBarIconView.STATE_HIDDEN, true /* animate */,
    255                             () -> removeTransientView(icon));
    256                 }
    257             }
    258         }
    259     }
    260 
    261     /**
    262      * Finds the first view with a translation bigger then a given value
    263      */
    264     private int findFirstViewIndexAfter(float translationX) {
    265         for (int i = 0; i < getChildCount(); i++) {
    266             View view = getChildAt(i);
    267             if (view.getTranslationX() > translationX) {
    268                 return i;
    269             }
    270         }
    271         return getChildCount();
    272     }
    273 
    274     public void resetViewStates() {
    275         for (int i = 0; i < getChildCount(); i++) {
    276             View view = getChildAt(i);
    277             ViewState iconState = mIconStates.get(view);
    278             iconState.initFrom(view);
    279             iconState.alpha = 1.0f;
    280             iconState.hidden = false;
    281         }
    282     }
    283 
    284     /**
    285      * Calulate the horizontal translations for each notification based on how much the icons
    286      * are inserted into the notification container.
    287      * If this is not a whole number, the fraction means by how much the icon is appearing.
    288      */
    289     public void calculateIconTranslations() {
    290         float translationX = getActualPaddingStart();
    291         int firstOverflowIndex = -1;
    292         int childCount = getChildCount();
    293         int maxVisibleIcons = mDark ? MAX_VISIBLE_ICONS_WHEN_DARK : childCount;
    294         float layoutEnd = getLayoutEnd();
    295         float overflowStart = layoutEnd - mIconSize * (2 + OVERFLOW_EARLY_AMOUNT);
    296         boolean hasAmbient = mSpeedBumpIndex != -1 && mSpeedBumpIndex < getChildCount();
    297         float visualOverflowStart = 0;
    298         for (int i = 0; i < childCount; i++) {
    299             View view = getChildAt(i);
    300             IconState iconState = mIconStates.get(view);
    301             iconState.xTranslation = translationX;
    302             boolean forceOverflow = mSpeedBumpIndex != -1 && i >= mSpeedBumpIndex
    303                     && iconState.iconAppearAmount > 0.0f || i >= maxVisibleIcons;
    304             boolean noOverflowAfter = i == childCount - 1;
    305             float drawingScale = mDark && view instanceof StatusBarIconView
    306                     ? ((StatusBarIconView) view).getIconScaleFullyDark()
    307                     : 1f;
    308             if (mOpenedAmount != 0.0f) {
    309                 noOverflowAfter = noOverflowAfter && !hasAmbient && !forceOverflow;
    310             }
    311             iconState.visibleState = StatusBarIconView.STATE_ICON;
    312             if (firstOverflowIndex == -1 && (forceOverflow
    313                     || (translationX >= (noOverflowAfter ? layoutEnd - mIconSize : overflowStart)))) {
    314                 firstOverflowIndex = noOverflowAfter && !forceOverflow ? i - 1 : i;
    315                 int totalDotLength = mStaticDotRadius * 6 + 2 * mDotPadding;
    316                 visualOverflowStart = overflowStart + mIconSize * (1 + OVERFLOW_EARLY_AMOUNT)
    317                         - totalDotLength / 2
    318                         - mIconSize * 0.5f + mStaticDotRadius;
    319                 if (forceOverflow) {
    320                     visualOverflowStart = Math.min(translationX, visualOverflowStart
    321                             + mStaticDotRadius * 2 + mDotPadding);
    322                 } else {
    323                     visualOverflowStart += (translationX - overflowStart) / mIconSize
    324                             * (mStaticDotRadius * 2 + mDotPadding);
    325                 }
    326                 if (mShowAllIcons) {
    327                     // We want to perfectly position the overflow in the static state, such that
    328                     // it's perfectly centered instead of measuring it from the end.
    329                     mVisualOverflowAdaption = 0;
    330                     if (firstOverflowIndex != -1) {
    331                         View firstOverflowView = getChildAt(i);
    332                         IconState overflowState = mIconStates.get(firstOverflowView);
    333                         float totalAmount = layoutEnd - overflowState.xTranslation;
    334                         float newPosition = overflowState.xTranslation + totalAmount / 2
    335                                 - totalDotLength / 2
    336                                 - mIconSize * 0.5f + mStaticDotRadius;
    337                         mVisualOverflowAdaption = newPosition - visualOverflowStart;
    338                         visualOverflowStart = newPosition;
    339                     }
    340                 } else {
    341                     visualOverflowStart += mVisualOverflowAdaption * (1f - mOpenedAmount);
    342                 }
    343             }
    344             translationX += iconState.iconAppearAmount * view.getWidth() * drawingScale;
    345         }
    346         if (firstOverflowIndex != -1) {
    347             int numDots = 1;
    348             translationX = visualOverflowStart;
    349             for (int i = firstOverflowIndex; i < childCount; i++) {
    350                 View view = getChildAt(i);
    351                 IconState iconState = mIconStates.get(view);
    352                 int dotWidth = mStaticDotRadius * 2 + mDotPadding;
    353                 iconState.xTranslation = translationX;
    354                 if (numDots <= 3) {
    355                     if (numDots == 1 && iconState.iconAppearAmount < 0.8f) {
    356                         iconState.visibleState = StatusBarIconView.STATE_ICON;
    357                         numDots--;
    358                     } else {
    359                         iconState.visibleState = StatusBarIconView.STATE_DOT;
    360                     }
    361                     translationX += (numDots == 3 ? 3 * dotWidth : dotWidth)
    362                             * iconState.iconAppearAmount;
    363                 } else {
    364                     iconState.visibleState = StatusBarIconView.STATE_HIDDEN;
    365                 }
    366                 numDots++;
    367             }
    368         }
    369         boolean center = mDark;
    370         if (center && translationX < getLayoutEnd()) {
    371             float delta = (getLayoutEnd() - translationX) / 2;
    372             if (firstOverflowIndex != -1) {
    373                 // If we have an overflow, only count those half for centering because the dots
    374                 // don't have a lot of visual weight.
    375                 float deltaIgnoringOverflow = (getLayoutEnd() - visualOverflowStart) / 2;
    376                 delta = (deltaIgnoringOverflow + delta) / 2;
    377             }
    378             for (int i = 0; i < childCount; i++) {
    379                 View view = getChildAt(i);
    380                 IconState iconState = mIconStates.get(view);
    381                 iconState.xTranslation += delta;
    382             }
    383         }
    384 
    385         if (isLayoutRtl()) {
    386             for (int i = 0; i < childCount; i++) {
    387                 View view = getChildAt(i);
    388                 IconState iconState = mIconStates.get(view);
    389                 iconState.xTranslation = getWidth() - iconState.xTranslation - view.getWidth();
    390             }
    391         }
    392     }
    393 
    394     private float getLayoutEnd() {
    395         return getActualWidth() - getActualPaddingEnd();
    396     }
    397 
    398     private float getActualPaddingEnd() {
    399         if (mActualPaddingEnd == NO_VALUE) {
    400             return getPaddingEnd();
    401         }
    402         return mActualPaddingEnd;
    403     }
    404 
    405     private float getActualPaddingStart() {
    406         if (mActualPaddingStart == NO_VALUE) {
    407             return getPaddingStart();
    408         }
    409         return mActualPaddingStart;
    410     }
    411 
    412     /**
    413      * Sets whether the layout should always show all icons.
    414      * If this is true, the icon positions will be updated on layout.
    415      * If this if false, the layout is managed from the outside and layouting won't trigger a
    416      * repositioning of the icons.
    417      */
    418     public void setShowAllIcons(boolean showAllIcons) {
    419         mShowAllIcons = showAllIcons;
    420     }
    421 
    422     public void setActualLayoutWidth(int actualLayoutWidth) {
    423         mActualLayoutWidth = actualLayoutWidth;
    424         if (DEBUG) {
    425             invalidate();
    426         }
    427     }
    428 
    429     public void setActualPaddingEnd(float paddingEnd) {
    430         mActualPaddingEnd = paddingEnd;
    431         if (DEBUG) {
    432             invalidate();
    433         }
    434     }
    435 
    436     public void setActualPaddingStart(float paddingStart) {
    437         mActualPaddingStart = paddingStart;
    438         if (DEBUG) {
    439             invalidate();
    440         }
    441     }
    442 
    443     public int getActualWidth() {
    444         if (mActualLayoutWidth == NO_VALUE) {
    445             return getWidth();
    446         }
    447         return mActualLayoutWidth;
    448     }
    449 
    450     public void setChangingViewPositions(boolean changingViewPositions) {
    451         mChangingViewPositions = changingViewPositions;
    452     }
    453 
    454     public void setDark(boolean dark, boolean fade, long delay) {
    455         mDark = dark;
    456         mDisallowNextAnimation |= !fade;
    457         for (int i = 0; i < getChildCount(); i++) {
    458             View view = getChildAt(i);
    459             if (view instanceof StatusBarIconView) {
    460                 ((StatusBarIconView) view).setDark(dark, fade, delay);
    461                 if (!dark && fade) {
    462                     getIconState((StatusBarIconView) view).justUndarkened = true;
    463                 }
    464             }
    465         }
    466     }
    467 
    468     public IconState getIconState(StatusBarIconView icon) {
    469         return mIconStates.get(icon);
    470     }
    471 
    472     public void setSpeedBumpIndex(int speedBumpIndex) {
    473         mSpeedBumpIndex = speedBumpIndex;
    474     }
    475 
    476     public void setOpenedAmount(float expandAmount) {
    477         mOpenedAmount = expandAmount;
    478     }
    479 
    480     public float getVisualOverflowAdaption() {
    481         return mVisualOverflowAdaption;
    482     }
    483 
    484     public void setVisualOverflowAdaption(float visualOverflowAdaption) {
    485         mVisualOverflowAdaption = visualOverflowAdaption;
    486     }
    487 
    488     public boolean hasOverflow() {
    489         float width = (getChildCount() + OVERFLOW_EARLY_AMOUNT) * mIconSize;
    490         return width - (getWidth() - getActualPaddingStart() - getActualPaddingEnd()) > 0;
    491     }
    492 
    493     public int getIconSize() {
    494         return mIconSize;
    495     }
    496 
    497     public void setAnimationsEnabled(boolean enabled) {
    498         if (!enabled && mAnimationsEnabled) {
    499             for (int i = 0; i < getChildCount(); i++) {
    500                 View child = getChildAt(i);
    501                 ViewState childState = mIconStates.get(child);
    502                 if (childState != null) {
    503                     childState.cancelAnimations(child);
    504                     childState.applyToView(child);
    505                 }
    506             }
    507         }
    508         mAnimationsEnabled = enabled;
    509     }
    510 
    511     public void setReplacingIcons(ArrayMap<String, ArrayList<StatusBarIcon>> replacingIcons) {
    512         mReplacingIcons = replacingIcons;
    513     }
    514 
    515     public class IconState extends ViewState {
    516         public float iconAppearAmount = 1.0f;
    517         public float clampedAppearAmount = 1.0f;
    518         public int visibleState;
    519         public boolean justAdded = true;
    520         private boolean justReplaced;
    521         public boolean needsCannedAnimation;
    522         public boolean useFullTransitionAmount;
    523         public boolean useLinearTransitionAmount;
    524         public boolean translateContent;
    525         public boolean justUndarkened;
    526         public int iconColor = StatusBarIconView.NO_COLOR;
    527         public boolean noAnimations;
    528 
    529         @Override
    530         public void applyToView(View view) {
    531             if (view instanceof StatusBarIconView) {
    532                 StatusBarIconView icon = (StatusBarIconView) view;
    533                 boolean animate = false;
    534                 AnimationProperties animationProperties = null;
    535                 boolean animationsAllowed = (mAnimationsEnabled || justUndarkened)
    536                         && !mDisallowNextAnimation
    537                         && !noAnimations;
    538                 if (animationsAllowed) {
    539                     if (justAdded || justReplaced) {
    540                         super.applyToView(icon);
    541                         if (justAdded && iconAppearAmount != 0.0f) {
    542                             icon.setAlpha(0.0f);
    543                             icon.setVisibleState(StatusBarIconView.STATE_HIDDEN,
    544                                     false /* animate */);
    545                             animationProperties = ADD_ICON_PROPERTIES;
    546                             animate = true;
    547                         }
    548                     } else if (justUndarkened) {
    549                         animationProperties = UNDARK_PROPERTIES;
    550                         animate = true;
    551                     } else if (visibleState != icon.getVisibleState()) {
    552                         animationProperties = DOT_ANIMATION_PROPERTIES;
    553                         animate = true;
    554                     }
    555                     if (!animate && mAddAnimationStartIndex >= 0
    556                             && indexOfChild(view) >= mAddAnimationStartIndex
    557                             && (icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN
    558                             || visibleState != StatusBarIconView.STATE_HIDDEN)) {
    559                         animationProperties = DOT_ANIMATION_PROPERTIES;
    560                         animate = true;
    561                     }
    562                     if (needsCannedAnimation) {
    563                         AnimationFilter animationFilter = mTempProperties.getAnimationFilter();
    564                         animationFilter.reset();
    565                         animationFilter.combineFilter(
    566                                 ICON_ANIMATION_PROPERTIES.getAnimationFilter());
    567                         mTempProperties.resetCustomInterpolators();
    568                         mTempProperties.combineCustomInterpolators(ICON_ANIMATION_PROPERTIES);
    569                         if (animationProperties != null) {
    570                             animationFilter.combineFilter(animationProperties.getAnimationFilter());
    571                             mTempProperties.combineCustomInterpolators(animationProperties);
    572                         }
    573                         animationProperties = mTempProperties;
    574                         animationProperties.setDuration(CANNED_ANIMATION_DURATION);
    575                         animate = true;
    576                         mCannedAnimationStartIndex = indexOfChild(view);
    577                     }
    578                     if (!animate && mCannedAnimationStartIndex >= 0
    579                             && indexOfChild(view) > mCannedAnimationStartIndex
    580                             && (icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN
    581                             || visibleState != StatusBarIconView.STATE_HIDDEN)) {
    582                         AnimationFilter animationFilter = mTempProperties.getAnimationFilter();
    583                         animationFilter.reset();
    584                         animationFilter.animateX();
    585                         mTempProperties.resetCustomInterpolators();
    586                         animationProperties = mTempProperties;
    587                         animationProperties.setDuration(CANNED_ANIMATION_DURATION);
    588                         animate = true;
    589                     }
    590                 }
    591                 icon.setVisibleState(visibleState, animationsAllowed);
    592                 icon.setIconColor(iconColor, needsCannedAnimation && animationsAllowed);
    593                 if (animate) {
    594                     animateTo(icon, animationProperties);
    595                 } else {
    596                     super.applyToView(view);
    597                 }
    598             }
    599             justAdded = false;
    600             justReplaced = false;
    601             needsCannedAnimation = false;
    602             justUndarkened = false;
    603         }
    604 
    605         @Override
    606         public void initFrom(View view) {
    607             super.initFrom(view);
    608             if (view instanceof StatusBarIconView) {
    609                 iconColor = ((StatusBarIconView) view).getStaticDrawableColor();
    610             }
    611         }
    612     }
    613 }
    614