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 NotificationIconContainer(Context context, AttributeSet attrs)127 public NotificationIconContainer(Context context, AttributeSet attrs) { 128 super(context, attrs); 129 initDimens(); 130 setWillNotDraw(!DEBUG); 131 } 132 initDimens()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 onDraw(Canvas canvas)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 onConfigurationChanged(Configuration newConfig)148 protected void onConfigurationChanged(Configuration newConfig) { 149 super.onConfigurationChanged(newConfig); 150 initDimens(); 151 } 152 @Override onLayout(boolean changed, int l, int t, int r, int b)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 applyIconStates()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 onViewAdded(View child)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 isReplacingIcon(View child)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 onViewRemoved(View child)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 */ findFirstViewIndexAfter(float translationX)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 resetViewStates()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 */ calculateIconTranslations()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 getLayoutEnd()394 private float getLayoutEnd() { 395 return getActualWidth() - getActualPaddingEnd(); 396 } 397 getActualPaddingEnd()398 private float getActualPaddingEnd() { 399 if (mActualPaddingEnd == NO_VALUE) { 400 return getPaddingEnd(); 401 } 402 return mActualPaddingEnd; 403 } 404 getActualPaddingStart()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 */ setShowAllIcons(boolean showAllIcons)418 public void setShowAllIcons(boolean showAllIcons) { 419 mShowAllIcons = showAllIcons; 420 } 421 setActualLayoutWidth(int actualLayoutWidth)422 public void setActualLayoutWidth(int actualLayoutWidth) { 423 mActualLayoutWidth = actualLayoutWidth; 424 if (DEBUG) { 425 invalidate(); 426 } 427 } 428 setActualPaddingEnd(float paddingEnd)429 public void setActualPaddingEnd(float paddingEnd) { 430 mActualPaddingEnd = paddingEnd; 431 if (DEBUG) { 432 invalidate(); 433 } 434 } 435 setActualPaddingStart(float paddingStart)436 public void setActualPaddingStart(float paddingStart) { 437 mActualPaddingStart = paddingStart; 438 if (DEBUG) { 439 invalidate(); 440 } 441 } 442 getActualWidth()443 public int getActualWidth() { 444 if (mActualLayoutWidth == NO_VALUE) { 445 return getWidth(); 446 } 447 return mActualLayoutWidth; 448 } 449 setChangingViewPositions(boolean changingViewPositions)450 public void setChangingViewPositions(boolean changingViewPositions) { 451 mChangingViewPositions = changingViewPositions; 452 } 453 setDark(boolean dark, boolean fade, long delay)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 getIconState(StatusBarIconView icon)468 public IconState getIconState(StatusBarIconView icon) { 469 return mIconStates.get(icon); 470 } 471 setSpeedBumpIndex(int speedBumpIndex)472 public void setSpeedBumpIndex(int speedBumpIndex) { 473 mSpeedBumpIndex = speedBumpIndex; 474 } 475 setOpenedAmount(float expandAmount)476 public void setOpenedAmount(float expandAmount) { 477 mOpenedAmount = expandAmount; 478 } 479 getVisualOverflowAdaption()480 public float getVisualOverflowAdaption() { 481 return mVisualOverflowAdaption; 482 } 483 setVisualOverflowAdaption(float visualOverflowAdaption)484 public void setVisualOverflowAdaption(float visualOverflowAdaption) { 485 mVisualOverflowAdaption = visualOverflowAdaption; 486 } 487 hasOverflow()488 public boolean hasOverflow() { 489 float width = (getChildCount() + OVERFLOW_EARLY_AMOUNT) * mIconSize; 490 return width - (getWidth() - getActualPaddingStart() - getActualPaddingEnd()) > 0; 491 } 492 getIconSize()493 public int getIconSize() { 494 return mIconSize; 495 } 496 setAnimationsEnabled(boolean enabled)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 setReplacingIcons(ArrayMap<String, ArrayList<StatusBarIcon>> replacingIcons)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 applyToView(View view)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 initFrom(View view)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