blob: a53ce9bb201401aa54d759cb18ff849c7a59f8e4 [file] [log] [blame]
Selim Cinek281c2022016-10-13 19:14:43 -07001/*
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
17package com.android.systemui.statusbar.phone;
18
Selim Cinek332c23f2018-03-16 17:37:50 -070019import static com.android.systemui.statusbar.phone.HeadsUpAppearanceController.CONTENT_FADE_DELAY;
Lucas Dupin23a8d3b2018-10-08 20:57:35 -070020import static com.android.systemui.statusbar.phone.HeadsUpAppearanceController.CONTENT_FADE_DURATION;
Selim Cinek332c23f2018-03-16 17:37:50 -070021
Selim Cinek281c2022016-10-13 19:14:43 -070022import android.content.Context;
Selim Cinek49014f82016-11-04 14:55:30 -070023import android.content.res.Configuration;
24import android.graphics.Canvas;
25import android.graphics.Color;
26import android.graphics.Paint;
Selim Cinekaa9db1f2018-02-27 17:35:47 -080027import android.graphics.Rect;
Selim Cinek72fc8db2017-06-06 18:07:47 -070028import android.graphics.drawable.Icon;
Selim Cinek281c2022016-10-13 19:14:43 -070029import android.util.AttributeSet;
30import android.view.View;
31
Lucas Dupin23a8d3b2018-10-08 20:57:35 -070032import androidx.collection.ArrayMap;
33
Selim Cinek72fc8db2017-06-06 18:07:47 -070034import com.android.internal.statusbar.StatusBarIcon;
Selim Cinek061d9072016-12-20 13:17:03 +010035import com.android.systemui.Interpolators;
Selim Cinek49014f82016-11-04 14:55:30 -070036import com.android.systemui.R;
Selim Cinek281c2022016-10-13 19:14:43 -070037import com.android.systemui.statusbar.AlphaOptimizedFrameLayout;
Selim Cinek49014f82016-11-04 14:55:30 -070038import com.android.systemui.statusbar.StatusBarIconView;
Rohan Shah20790b82018-07-02 17:21:04 -070039import com.android.systemui.statusbar.notification.stack.AnimationFilter;
40import com.android.systemui.statusbar.notification.stack.AnimationProperties;
41import com.android.systemui.statusbar.notification.stack.ViewState;
Selim Cinek281c2022016-10-13 19:14:43 -070042
Selim Cinek72fc8db2017-06-06 18:07:47 -070043import java.util.ArrayList;
Selim Cinek65d418e2016-11-29 15:42:34 -080044import java.util.HashMap;
Selim Cinek281c2022016-10-13 19:14:43 -070045
46/**
47 * A container for notification icons. It handles overflowing icons properly and positions them
48 * correctly on the screen.
49 */
50public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
Selim Cinek932005d2016-12-05 17:12:09 -080051 /**
52 * A float value indicating how much before the overflow start the icons should transform into
53 * a dot. A value of 0 means that they are exactly at the end and a value of 1 means it starts
54 * 1 icon width early.
55 */
56 public static final float OVERFLOW_EARLY_AMOUNT = 0.2f;
57 private static final int NO_VALUE = Integer.MIN_VALUE;
Selim Cinek281c2022016-10-13 19:14:43 -070058 private static final String TAG = "NotificationIconContainer";
Selim Cinek49014f82016-11-04 14:55:30 -070059 private static final boolean DEBUG = false;
Evan Laird8cf0de42018-02-06 18:34:55 -050060 private static final boolean DEBUG_OVERFLOW = false;
Selim Cinek2b549f42016-11-22 16:38:51 -080061 private static final int CANNED_ANIMATION_DURATION = 100;
Selim Cinek49014f82016-11-04 14:55:30 -070062 private static final AnimationProperties DOT_ANIMATION_PROPERTIES = new AnimationProperties() {
Selim Cinek5b5beb012016-11-08 18:11:58 -080063 private AnimationFilter mAnimationFilter = new AnimationFilter().animateX();
64
Selim Cinek49014f82016-11-04 14:55:30 -070065 @Override
66 public AnimationFilter getAnimationFilter() {
Selim Cinek5b5beb012016-11-08 18:11:58 -080067 return mAnimationFilter;
Selim Cinek49014f82016-11-04 14:55:30 -070068 }
69 }.setDuration(200);
Selim Cinekb3dadcc2016-11-21 17:21:13 -080070
Selim Cinek2b549f42016-11-22 16:38:51 -080071 private static final AnimationProperties ICON_ANIMATION_PROPERTIES = new AnimationProperties() {
Selim Cinekf082fe22016-12-20 14:32:19 +010072 private AnimationFilter mAnimationFilter = new AnimationFilter().animateY().animateAlpha()
73 .animateScale();
Selim Cinek2b549f42016-11-22 16:38:51 -080074
75 @Override
76 public AnimationFilter getAnimationFilter() {
77 return mAnimationFilter;
78 }
Selim Cinek061d9072016-12-20 13:17:03 +010079
80 }.setDuration(CANNED_ANIMATION_DURATION)
81 .setCustomInterpolator(View.TRANSLATION_Y, Interpolators.ICON_OVERSHOT);
Selim Cinek2b549f42016-11-22 16:38:51 -080082
Lucas Dupin8274aa92017-09-13 17:24:09 -070083 /**
84 * Temporary AnimationProperties to avoid unnecessary allocations.
85 */
86 private static final AnimationProperties sTempProperties = new AnimationProperties() {
Selim Cinek2b549f42016-11-22 16:38:51 -080087 private AnimationFilter mAnimationFilter = new AnimationFilter();
Selim Cinek2b549f42016-11-22 16:38:51 -080088
89 @Override
90 public AnimationFilter getAnimationFilter() {
91 return mAnimationFilter;
92 }
Selim Cinekf082fe22016-12-20 14:32:19 +010093 };
Selim Cinek2b549f42016-11-22 16:38:51 -080094
Selim Cinek5b5beb012016-11-08 18:11:58 -080095 private static final AnimationProperties ADD_ICON_PROPERTIES = new AnimationProperties() {
96 private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha();
97
98 @Override
99 public AnimationFilter getAnimationFilter() {
100 return mAnimationFilter;
101 }
102 }.setDuration(200).setDelay(50);
Adrian Roos28f90c72017-05-08 17:24:26 -0700103
Selim Cinekd03518c2018-03-15 12:13:51 -0700104 /**
105 * The animation property used for all icons that were not isolated, when the isolation ends.
106 * This just fades the alpha and doesn't affect the movement and has a delay.
107 */
108 private static final AnimationProperties UNISOLATION_PROPERTY_OTHERS
109 = new AnimationProperties() {
110 private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha();
111
112 @Override
113 public AnimationFilter getAnimationFilter() {
114 return mAnimationFilter;
115 }
Selim Cinek332c23f2018-03-16 17:37:50 -0700116 }.setDuration(CONTENT_FADE_DURATION);
Selim Cinekd03518c2018-03-15 12:13:51 -0700117
118 /**
119 * The animation property used for the icon when its isolation ends.
120 * This animates the translation back to the right position.
121 */
122 private static final AnimationProperties UNISOLATION_PROPERTY = new AnimationProperties() {
123 private AnimationFilter mAnimationFilter = new AnimationFilter().animateX();
124
125 @Override
126 public AnimationFilter getAnimationFilter() {
127 return mAnimationFilter;
128 }
Selim Cinek332c23f2018-03-16 17:37:50 -0700129 }.setDuration(CONTENT_FADE_DURATION);
Selim Cinekd03518c2018-03-15 12:13:51 -0700130
Selim Cinek195dfc52019-05-30 19:35:05 -0700131 private static final int MAX_VISIBLE_ICONS_ON_LOCK = 5;
Evan Lairdc987fc72017-12-15 10:14:22 -0500132 public static final int MAX_STATIC_ICONS = 4;
Evan Laird6f9e8562018-05-22 19:14:50 -0400133 private static final int MAX_DOTS = 1;
Selim Cinek281c2022016-10-13 19:14:43 -0700134
Evan Lairdc987fc72017-12-15 10:14:22 -0500135 private boolean mIsStaticLayout = true;
Selim Cinek65d418e2016-11-29 15:42:34 -0800136 private final HashMap<View, IconState> mIconStates = new HashMap<>();
Selim Cinek49014f82016-11-04 14:55:30 -0700137 private int mDotPadding;
138 private int mStaticDotRadius;
Evan Laird8cf0de42018-02-06 18:34:55 -0500139 private int mStaticDotDiameter;
Evan Laird6f9e8562018-05-22 19:14:50 -0400140 private int mOverflowWidth;
Selim Cinek932005d2016-12-05 17:12:09 -0800141 private int mActualLayoutWidth = NO_VALUE;
142 private float mActualPaddingEnd = NO_VALUE;
143 private float mActualPaddingStart = NO_VALUE;
Selim Cinek195dfc52019-05-30 19:35:05 -0700144 private boolean mDozing;
145 private boolean mOnLockScreen;
Selim Cinek5b5beb012016-11-08 18:11:58 -0800146 private boolean mChangingViewPositions;
Selim Cinek2b549f42016-11-22 16:38:51 -0800147 private int mAddAnimationStartIndex = -1;
148 private int mCannedAnimationStartIndex = -1;
Selim Cinek17e1b692016-12-02 18:19:11 -0800149 private int mSpeedBumpIndex = -1;
150 private int mIconSize;
151 private float mOpenedAmount = 0.0f;
Adrian Roosff160ed2017-01-13 17:18:29 -0800152 private boolean mDisallowNextAnimation;
Selim Cinek09bd29d2017-02-03 15:30:28 -0800153 private boolean mAnimationsEnabled = true;
Selim Cinek72fc8db2017-06-06 18:07:47 -0700154 private ArrayMap<String, ArrayList<StatusBarIcon>> mReplacingIcons;
Evan Lairdc987fc72017-12-15 10:14:22 -0500155 // Keep track of the last visible icon so collapsed container can report on its location
156 private IconState mLastVisibleIconState;
Lucas Dupin15475702018-04-04 14:32:53 -0700157 private IconState mFirstVisibleIconState;
Evan Laird8cf0de42018-02-06 18:34:55 -0500158 private float mVisualOverflowStart;
159 // Keep track of overflow in range [0, 3]
160 private int mNumDots;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800161 private StatusBarIconView mIsolatedIcon;
162 private Rect mIsolatedIconLocation;
163 private int[] mAbsolutePosition = new int[2];
Selim Cinekd03518c2018-03-15 12:13:51 -0700164 private View mIsolatedIconForAnimation;
Evan Lairdc987fc72017-12-15 10:14:22 -0500165
Selim Cinek281c2022016-10-13 19:14:43 -0700166 public NotificationIconContainer(Context context, AttributeSet attrs) {
167 super(context, attrs);
Selim Cinek49014f82016-11-04 14:55:30 -0700168 initDimens();
Evan Laird8cf0de42018-02-06 18:34:55 -0500169 setWillNotDraw(!(DEBUG || DEBUG_OVERFLOW));
Selim Cinek281c2022016-10-13 19:14:43 -0700170 }
171
Selim Cinek49014f82016-11-04 14:55:30 -0700172 private void initDimens() {
173 mDotPadding = getResources().getDimensionPixelSize(R.dimen.overflow_icon_dot_padding);
174 mStaticDotRadius = getResources().getDimensionPixelSize(R.dimen.overflow_dot_radius);
Evan Laird8cf0de42018-02-06 18:34:55 -0500175 mStaticDotDiameter = 2 * mStaticDotRadius;
Selim Cinek49014f82016-11-04 14:55:30 -0700176 }
177
178 @Override
179 protected void onDraw(Canvas canvas) {
180 super.onDraw(canvas);
181 Paint paint = new Paint();
182 paint.setColor(Color.RED);
183 paint.setStyle(Paint.Style.STROKE);
184 canvas.drawRect(getActualPaddingStart(), 0, getLayoutEnd(), getHeight(), paint);
Evan Laird8cf0de42018-02-06 18:34:55 -0500185
186 if (DEBUG_OVERFLOW) {
187 if (mLastVisibleIconState == null) {
188 return;
189 }
190
191 int height = getHeight();
192 int end = getFinalTranslationX();
193
194 // Visualize the "end" of the layout
195 paint.setColor(Color.BLUE);
196 canvas.drawLine(end, 0, end, height, paint);
197
Lucas Dupin15475702018-04-04 14:32:53 -0700198 paint.setColor(Color.GREEN);
Evan Laird8cf0de42018-02-06 18:34:55 -0500199 int lastIcon = (int) mLastVisibleIconState.xTranslation;
200 canvas.drawLine(lastIcon, 0, lastIcon, height, paint);
201
Lucas Dupin15475702018-04-04 14:32:53 -0700202 if (mFirstVisibleIconState != null) {
203 int firstIcon = (int) mFirstVisibleIconState.xTranslation;
204 canvas.drawLine(firstIcon, 0, firstIcon, height, paint);
205 }
206
Evan Laird8cf0de42018-02-06 18:34:55 -0500207 paint.setColor(Color.RED);
208 canvas.drawLine(mVisualOverflowStart, 0, mVisualOverflowStart, height, paint);
209
210 paint.setColor(Color.YELLOW);
211 float overflow = getMaxOverflowStart();
212 canvas.drawLine(overflow, 0, overflow, height, paint);
213 }
Selim Cinek49014f82016-11-04 14:55:30 -0700214 }
215
216 @Override
217 protected void onConfigurationChanged(Configuration newConfig) {
218 super.onConfigurationChanged(newConfig);
219 initDimens();
220 }
Lucas Dupin15475702018-04-04 14:32:53 -0700221
Selim Cinek281c2022016-10-13 19:14:43 -0700222 @Override
223 protected void onLayout(boolean changed, int l, int t, int r, int b) {
224 float centerY = getHeight() / 2.0f;
225 // we layout all our children on the left at the top
Selim Cinek17e1b692016-12-02 18:19:11 -0800226 mIconSize = 0;
Selim Cinek281c2022016-10-13 19:14:43 -0700227 for (int i = 0; i < getChildCount(); i++) {
228 View child = getChildAt(i);
229 // We need to layout all children even the GONE ones, such that the heights are
230 // calculated correctly as they are used to calculate how many we can fit on the screen
231 int width = child.getMeasuredWidth();
232 int height = child.getMeasuredHeight();
233 int top = (int) (centerY - height / 2.0f);
234 child.layout(0, top, width, top + height);
Selim Cinek17e1b692016-12-02 18:19:11 -0800235 if (i == 0) {
Evan Laird6f9e8562018-05-22 19:14:50 -0400236 setIconSize(child.getWidth());
Selim Cinek17e1b692016-12-02 18:19:11 -0800237 }
Selim Cinek281c2022016-10-13 19:14:43 -0700238 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800239 getLocationOnScreen(mAbsolutePosition);
Evan Lairdc987fc72017-12-15 10:14:22 -0500240 if (mIsStaticLayout) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800241 updateState();
Selim Cinek281c2022016-10-13 19:14:43 -0700242 }
243 }
244
Evan Laird6f9e8562018-05-22 19:14:50 -0400245 private void setIconSize(int size) {
246 mIconSize = size;
247 mOverflowWidth = mIconSize + (MAX_DOTS - 1) * (mStaticDotDiameter + mDotPadding);
248 }
249
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800250 private void updateState() {
251 resetViewStates();
252 calculateIconTranslations();
253 applyIconStates();
254 }
255
Selim Cinekc383fd02016-10-21 15:31:26 -0700256 public void applyIconStates() {
Selim Cinek281c2022016-10-13 19:14:43 -0700257 for (int i = 0; i < getChildCount(); i++) {
258 View child = getChildAt(i);
Selim Cinekc383fd02016-10-21 15:31:26 -0700259 ViewState childState = mIconStates.get(child);
Selim Cinek281c2022016-10-13 19:14:43 -0700260 if (childState != null) {
261 childState.applyToView(child);
262 }
263 }
Selim Cinek2b549f42016-11-22 16:38:51 -0800264 mAddAnimationStartIndex = -1;
265 mCannedAnimationStartIndex = -1;
Adrian Roosff160ed2017-01-13 17:18:29 -0800266 mDisallowNextAnimation = false;
Selim Cinekd03518c2018-03-15 12:13:51 -0700267 mIsolatedIconForAnimation = null;
Selim Cinek281c2022016-10-13 19:14:43 -0700268 }
269
270 @Override
271 public void onViewAdded(View child) {
272 super.onViewAdded(child);
Selim Cinek72fc8db2017-06-06 18:07:47 -0700273 boolean isReplacingIcon = isReplacingIcon(child);
Selim Cinek5b5beb012016-11-08 18:11:58 -0800274 if (!mChangingViewPositions) {
Selim Cinek72fc8db2017-06-06 18:07:47 -0700275 IconState v = new IconState();
276 if (isReplacingIcon) {
277 v.justAdded = false;
278 v.justReplaced = true;
279 }
280 mIconStates.put(child, v);
Selim Cinek5b5beb012016-11-08 18:11:58 -0800281 }
282 int childIndex = indexOfChild(child);
Selim Cinek72fc8db2017-06-06 18:07:47 -0700283 if (childIndex < getChildCount() - 1 && !isReplacingIcon
Selim Cinek5b5beb012016-11-08 18:11:58 -0800284 && mIconStates.get(getChildAt(childIndex + 1)).iconAppearAmount > 0.0f) {
Selim Cinek2b549f42016-11-22 16:38:51 -0800285 if (mAddAnimationStartIndex < 0) {
286 mAddAnimationStartIndex = childIndex;
Selim Cinek5b5beb012016-11-08 18:11:58 -0800287 } else {
Selim Cinek2b549f42016-11-22 16:38:51 -0800288 mAddAnimationStartIndex = Math.min(mAddAnimationStartIndex, childIndex);
Selim Cinek5b5beb012016-11-08 18:11:58 -0800289 }
290 }
Selim Cinek04adab52017-11-28 18:22:12 +0100291 if (child instanceof StatusBarIconView) {
Selim Cinek195dfc52019-05-30 19:35:05 -0700292 ((StatusBarIconView) child).setDozing(mDozing, false, 0);
Adrian Roos456e0052017-04-04 16:44:29 -0700293 }
Selim Cinek281c2022016-10-13 19:14:43 -0700294 }
295
Selim Cinek72fc8db2017-06-06 18:07:47 -0700296 private boolean isReplacingIcon(View child) {
297 if (mReplacingIcons == null) {
298 return false;
299 }
300 if (!(child instanceof StatusBarIconView)) {
301 return false;
302 }
303 StatusBarIconView iconView = (StatusBarIconView) child;
304 Icon sourceIcon = iconView.getSourceIcon();
305 String groupKey = iconView.getNotification().getGroupKey();
306 ArrayList<StatusBarIcon> statusBarIcons = mReplacingIcons.get(groupKey);
307 if (statusBarIcons != null) {
308 StatusBarIcon replacedIcon = statusBarIcons.get(0);
309 if (sourceIcon.sameAs(replacedIcon.icon)) {
310 return true;
311 }
312 }
313 return false;
314 }
315
Selim Cinek281c2022016-10-13 19:14:43 -0700316 @Override
317 public void onViewRemoved(View child) {
318 super.onViewRemoved(child);
Evan Lairdfab34162019-06-13 16:29:20 -0400319
320 if (child instanceof StatusBarIconView) {
Selim Cinek72fc8db2017-06-06 18:07:47 -0700321 boolean isReplacingIcon = isReplacingIcon(child);
Selim Cinek5b5beb012016-11-08 18:11:58 -0800322 final StatusBarIconView icon = (StatusBarIconView) child;
Selim Cinekc7e4cb52019-06-20 15:41:45 -0700323 if (areAnimationsEnabled(icon) && icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN
Selim Cinek72fc8db2017-06-06 18:07:47 -0700324 && child.getVisibility() == VISIBLE && isReplacingIcon) {
Selim Cinek5b5beb012016-11-08 18:11:58 -0800325 int animationStartIndex = findFirstViewIndexAfter(icon.getTranslationX());
Selim Cinek2b549f42016-11-22 16:38:51 -0800326 if (mAddAnimationStartIndex < 0) {
327 mAddAnimationStartIndex = animationStartIndex;
Selim Cinek5b5beb012016-11-08 18:11:58 -0800328 } else {
Selim Cinek2b549f42016-11-22 16:38:51 -0800329 mAddAnimationStartIndex = Math.min(mAddAnimationStartIndex, animationStartIndex);
Selim Cinek5b5beb012016-11-08 18:11:58 -0800330 }
331 }
332 if (!mChangingViewPositions) {
333 mIconStates.remove(child);
Selim Cinekc7e4cb52019-06-20 15:41:45 -0700334 if (areAnimationsEnabled(icon) && !isReplacingIcon) {
Selim Cinek72fc8db2017-06-06 18:07:47 -0700335 addTransientView(icon, 0);
Selim Cinekd03518c2018-03-15 12:13:51 -0700336 boolean isIsolatedIcon = child == mIsolatedIcon;
Selim Cinek72fc8db2017-06-06 18:07:47 -0700337 icon.setVisibleState(StatusBarIconView.STATE_HIDDEN, true /* animate */,
Selim Cinekd03518c2018-03-15 12:13:51 -0700338 () -> removeTransientView(icon),
Selim Cinek332c23f2018-03-16 17:37:50 -0700339 isIsolatedIcon ? CONTENT_FADE_DURATION : 0);
Selim Cinek72fc8db2017-06-06 18:07:47 -0700340 }
Selim Cinek5b5beb012016-11-08 18:11:58 -0800341 }
342 }
343 }
344
Selim Cinekc7e4cb52019-06-20 15:41:45 -0700345 private boolean areAnimationsEnabled(StatusBarIconView icon) {
346 return mAnimationsEnabled || icon == mIsolatedIcon;
347 }
348
Selim Cinek5b5beb012016-11-08 18:11:58 -0800349 /**
350 * Finds the first view with a translation bigger then a given value
351 */
352 private int findFirstViewIndexAfter(float translationX) {
353 for (int i = 0; i < getChildCount(); i++) {
354 View view = getChildAt(i);
355 if (view.getTranslationX() > translationX) {
356 return i;
357 }
358 }
359 return getChildCount();
Selim Cinek281c2022016-10-13 19:14:43 -0700360 }
361
Selim Cinek65d418e2016-11-29 15:42:34 -0800362 public void resetViewStates() {
Selim Cinek281c2022016-10-13 19:14:43 -0700363 for (int i = 0; i < getChildCount(); i++) {
364 View view = getChildAt(i);
365 ViewState iconState = mIconStates.get(view);
366 iconState.initFrom(view);
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800367 iconState.alpha = mIsolatedIcon == null || view == mIsolatedIcon ? 1.0f : 0.0f;
Selim Cinek414ad332017-02-24 19:06:12 -0800368 iconState.hidden = false;
Selim Cinek281c2022016-10-13 19:14:43 -0700369 }
Selim Cinek281c2022016-10-13 19:14:43 -0700370 }
371
Selim Cinekc383fd02016-10-21 15:31:26 -0700372 /**
Evan Laird8cf0de42018-02-06 18:34:55 -0500373 * Calculate the horizontal translations for each notification based on how much the icons
Selim Cinekc383fd02016-10-21 15:31:26 -0700374 * are inserted into the notification container.
375 * If this is not a whole number, the fraction means by how much the icon is appearing.
376 */
Selim Cinek49014f82016-11-04 14:55:30 -0700377 public void calculateIconTranslations() {
378 float translationX = getActualPaddingStart();
Selim Cinek17e1b692016-12-02 18:19:11 -0800379 int firstOverflowIndex = -1;
Selim Cinekc383fd02016-10-21 15:31:26 -0700380 int childCount = getChildCount();
Selim Cinek195dfc52019-05-30 19:35:05 -0700381 int maxVisibleIcons = mOnLockScreen ? MAX_VISIBLE_ICONS_ON_LOCK :
Lucas Dupin7fc9dc12019-01-03 09:19:43 -0800382 mIsStaticLayout ? MAX_STATIC_ICONS : childCount;
Selim Cinek17e1b692016-12-02 18:19:11 -0800383 float layoutEnd = getLayoutEnd();
Evan Laird8cf0de42018-02-06 18:34:55 -0500384 float overflowStart = getMaxOverflowStart();
385 mVisualOverflowStart = 0;
Lucas Dupine87bf772018-04-05 12:48:43 -0700386 mFirstVisibleIconState = null;
Selim Cinek17e1b692016-12-02 18:19:11 -0800387 boolean hasAmbient = mSpeedBumpIndex != -1 && mSpeedBumpIndex < getChildCount();
Selim Cinekc383fd02016-10-21 15:31:26 -0700388 for (int i = 0; i < childCount; i++) {
389 View view = getChildAt(i);
390 IconState iconState = mIconStates.get(view);
391 iconState.xTranslation = translationX;
Lucas Dupine87bf772018-04-05 12:48:43 -0700392 if (mFirstVisibleIconState == null) {
393 mFirstVisibleIconState = iconState;
394 }
Adrian Roos14d70cb2017-04-25 16:46:49 -0700395 boolean forceOverflow = mSpeedBumpIndex != -1 && i >= mSpeedBumpIndex
396 && iconState.iconAppearAmount > 0.0f || i >= maxVisibleIcons;
Selim Cinek17e1b692016-12-02 18:19:11 -0800397 boolean noOverflowAfter = i == childCount - 1;
Selim Cinek195dfc52019-05-30 19:35:05 -0700398 float drawingScale = mOnLockScreen && view instanceof StatusBarIconView
399 ? ((StatusBarIconView) view).getIconScaleIncreased()
Lucas Dupin7fc9dc12019-01-03 09:19:43 -0800400 : 1f;
Selim Cinek17e1b692016-12-02 18:19:11 -0800401 if (mOpenedAmount != 0.0f) {
Adrian Roos14d70cb2017-04-25 16:46:49 -0700402 noOverflowAfter = noOverflowAfter && !hasAmbient && !forceOverflow;
Selim Cinek49014f82016-11-04 14:55:30 -0700403 }
Selim Cinek17e1b692016-12-02 18:19:11 -0800404 iconState.visibleState = StatusBarIconView.STATE_ICON;
Evan Laird8cf0de42018-02-06 18:34:55 -0500405
406 boolean isOverflowing =
Evan Laird6f9e8562018-05-22 19:14:50 -0400407 (translationX > (noOverflowAfter ? layoutEnd - mIconSize
408 : overflowStart - mIconSize));
Evan Laird8cf0de42018-02-06 18:34:55 -0500409 if (firstOverflowIndex == -1 && (forceOverflow || isOverflowing)) {
Adrian Roos14d70cb2017-04-25 16:46:49 -0700410 firstOverflowIndex = noOverflowAfter && !forceOverflow ? i - 1 : i;
Evan Laird6f9e8562018-05-22 19:14:50 -0400411 mVisualOverflowStart = layoutEnd - mOverflowWidth;
412 if (forceOverflow || mIsStaticLayout) {
Evan Laird8cf0de42018-02-06 18:34:55 -0500413 mVisualOverflowStart = Math.min(translationX, mVisualOverflowStart);
Selim Cinek17e1b692016-12-02 18:19:11 -0800414 }
415 }
Lucas Dupin7fc9dc12019-01-03 09:19:43 -0800416 translationX += iconState.iconAppearAmount * view.getWidth() * drawingScale;
Selim Cinekc383fd02016-10-21 15:31:26 -0700417 }
Evan Laird8cf0de42018-02-06 18:34:55 -0500418 mNumDots = 0;
Selim Cinek17e1b692016-12-02 18:19:11 -0800419 if (firstOverflowIndex != -1) {
Evan Laird8cf0de42018-02-06 18:34:55 -0500420 translationX = mVisualOverflowStart;
Selim Cinek17e1b692016-12-02 18:19:11 -0800421 for (int i = firstOverflowIndex; i < childCount; i++) {
Selim Cinek49014f82016-11-04 14:55:30 -0700422 View view = getChildAt(i);
423 IconState iconState = mIconStates.get(view);
Evan Laird6f9e8562018-05-22 19:14:50 -0400424 int dotWidth = mStaticDotDiameter + mDotPadding;
Selim Cinek49014f82016-11-04 14:55:30 -0700425 iconState.xTranslation = translationX;
Evan Laird8cf0de42018-02-06 18:34:55 -0500426 if (mNumDots < MAX_DOTS) {
427 if (mNumDots == 0 && iconState.iconAppearAmount < 0.8f) {
Selim Cinek17e1b692016-12-02 18:19:11 -0800428 iconState.visibleState = StatusBarIconView.STATE_ICON;
Selim Cinek17e1b692016-12-02 18:19:11 -0800429 } else {
430 iconState.visibleState = StatusBarIconView.STATE_DOT;
Evan Laird8cf0de42018-02-06 18:34:55 -0500431 mNumDots++;
Selim Cinek17e1b692016-12-02 18:19:11 -0800432 }
Evan Laird8cf0de42018-02-06 18:34:55 -0500433 translationX += (mNumDots == MAX_DOTS ? MAX_DOTS * dotWidth : dotWidth)
Selim Cinek17e1b692016-12-02 18:19:11 -0800434 * iconState.iconAppearAmount;
Evan Lairdc987fc72017-12-15 10:14:22 -0500435 mLastVisibleIconState = iconState;
Selim Cinek49014f82016-11-04 14:55:30 -0700436 } else {
437 iconState.visibleState = StatusBarIconView.STATE_HIDDEN;
438 }
Selim Cinek49014f82016-11-04 14:55:30 -0700439 }
Evan Lairdc987fc72017-12-15 10:14:22 -0500440 } else if (childCount > 0) {
441 View lastChild = getChildAt(childCount - 1);
442 mLastVisibleIconState = mIconStates.get(lastChild);
Lucas Dupin15475702018-04-04 14:32:53 -0700443 mFirstVisibleIconState = mIconStates.get(getChildAt(0));
Selim Cinek49014f82016-11-04 14:55:30 -0700444 }
Adrian Roos7a9551a2017-01-11 12:27:49 -0800445
Selim Cinek195dfc52019-05-30 19:35:05 -0700446 boolean center = mOnLockScreen;
Lucas Dupin7fc9dc12019-01-03 09:19:43 -0800447 if (center && translationX < getLayoutEnd()) {
448 float initialTranslation =
449 mFirstVisibleIconState == null ? 0 : mFirstVisibleIconState.xTranslation;
450
451 float contentWidth = 0;
452 if (mLastVisibleIconState != null) {
453 contentWidth = mLastVisibleIconState.xTranslation + mIconSize;
454 contentWidth = Math.min(getWidth(), contentWidth) - initialTranslation;
455 }
456 float availableSpace = getLayoutEnd() - getActualPaddingStart();
457 float delta = (availableSpace - contentWidth) / 2;
458
459 if (firstOverflowIndex != -1) {
460 // If we have an overflow, only count those half for centering because the dots
461 // don't have a lot of visual weight.
462 float deltaIgnoringOverflow = (getLayoutEnd() - mVisualOverflowStart) / 2;
463 delta = (deltaIgnoringOverflow + delta) / 2;
464 }
465 for (int i = 0; i < childCount; i++) {
466 View view = getChildAt(i);
467 IconState iconState = mIconStates.get(view);
468 iconState.xTranslation += delta;
469 }
470 }
471
Selim Cinek49014f82016-11-04 14:55:30 -0700472 if (isLayoutRtl()) {
473 for (int i = 0; i < childCount; i++) {
474 View view = getChildAt(i);
475 IconState iconState = mIconStates.get(view);
476 iconState.xTranslation = getWidth() - iconState.xTranslation - view.getWidth();
477 }
478 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800479 if (mIsolatedIcon != null) {
480 IconState iconState = mIconStates.get(mIsolatedIcon);
481 if (iconState != null) {
482 // Most of the time the icon isn't yet added when this is called but only happening
483 // later
484 iconState.xTranslation = mIsolatedIconLocation.left - mAbsolutePosition[0]
485 - (1 - mIsolatedIcon.getIconScale()) * mIsolatedIcon.getWidth() / 2.0f;
486 iconState.visibleState = StatusBarIconView.STATE_ICON;
487 }
488 }
Selim Cinek49014f82016-11-04 14:55:30 -0700489 }
490
491 private float getLayoutEnd() {
492 return getActualWidth() - getActualPaddingEnd();
493 }
494
495 private float getActualPaddingEnd() {
Selim Cinek932005d2016-12-05 17:12:09 -0800496 if (mActualPaddingEnd == NO_VALUE) {
Selim Cinek49014f82016-11-04 14:55:30 -0700497 return getPaddingEnd();
498 }
499 return mActualPaddingEnd;
500 }
501
502 private float getActualPaddingStart() {
Selim Cinek932005d2016-12-05 17:12:09 -0800503 if (mActualPaddingStart == NO_VALUE) {
Selim Cinek49014f82016-11-04 14:55:30 -0700504 return getPaddingStart();
505 }
506 return mActualPaddingStart;
Selim Cinek281c2022016-10-13 19:14:43 -0700507 }
508
509 /**
Evan Lairdc987fc72017-12-15 10:14:22 -0500510 * Sets whether the layout should always show the same number of icons.
Selim Cinek281c2022016-10-13 19:14:43 -0700511 * If this is true, the icon positions will be updated on layout.
512 * If this if false, the layout is managed from the outside and layouting won't trigger a
513 * repositioning of the icons.
514 */
Evan Lairdc987fc72017-12-15 10:14:22 -0500515 public void setIsStaticLayout(boolean isStaticLayout) {
516 mIsStaticLayout = isStaticLayout;
Selim Cinek281c2022016-10-13 19:14:43 -0700517 }
Selim Cinekc383fd02016-10-21 15:31:26 -0700518
Selim Cinek49014f82016-11-04 14:55:30 -0700519 public void setActualLayoutWidth(int actualLayoutWidth) {
520 mActualLayoutWidth = actualLayoutWidth;
521 if (DEBUG) {
522 invalidate();
523 }
524 }
525
526 public void setActualPaddingEnd(float paddingEnd) {
527 mActualPaddingEnd = paddingEnd;
528 if (DEBUG) {
529 invalidate();
530 }
531 }
532
533 public void setActualPaddingStart(float paddingStart) {
534 mActualPaddingStart = paddingStart;
535 if (DEBUG) {
536 invalidate();
537 }
538 }
539
540 public int getActualWidth() {
Selim Cinek932005d2016-12-05 17:12:09 -0800541 if (mActualLayoutWidth == NO_VALUE) {
Selim Cinek49014f82016-11-04 14:55:30 -0700542 return getWidth();
543 }
544 return mActualLayoutWidth;
545 }
546
Evan Lairdc987fc72017-12-15 10:14:22 -0500547 public int getFinalTranslationX() {
548 if (mLastVisibleIconState == null) {
549 return 0;
550 }
551
felkachangb6233432018-07-26 19:06:30 +0800552 int translation = (int) (isLayoutRtl() ? getWidth() - mLastVisibleIconState.xTranslation
553 : mLastVisibleIconState.xTranslation + mIconSize);
Evan Laird8cf0de42018-02-06 18:34:55 -0500554 // There's a chance that last translation goes beyond the edge maybe
555 return Math.min(getWidth(), translation);
556 }
557
558 private float getMaxOverflowStart() {
Evan Laird6f9e8562018-05-22 19:14:50 -0400559 return getLayoutEnd() - mOverflowWidth;
Evan Lairdc987fc72017-12-15 10:14:22 -0500560 }
561
Selim Cinek5b5beb012016-11-08 18:11:58 -0800562 public void setChangingViewPositions(boolean changingViewPositions) {
563 mChangingViewPositions = changingViewPositions;
564 }
565
Selim Cinek195dfc52019-05-30 19:35:05 -0700566 public void setDozing(boolean dozing, boolean fade, long delay) {
567 mDozing = dozing;
Adrian Roos28f90c72017-05-08 17:24:26 -0700568 mDisallowNextAnimation |= !fade;
Adrian Roos456e0052017-04-04 16:44:29 -0700569 for (int i = 0; i < getChildCount(); i++) {
570 View view = getChildAt(i);
571 if (view instanceof StatusBarIconView) {
Selim Cinek195dfc52019-05-30 19:35:05 -0700572 ((StatusBarIconView) view).setDozing(dozing, fade, delay);
Adrian Roos456e0052017-04-04 16:44:29 -0700573 }
574 }
Adrian Roos7a9551a2017-01-11 12:27:49 -0800575 }
576
Selim Cinek2b549f42016-11-22 16:38:51 -0800577 public IconState getIconState(StatusBarIconView icon) {
578 return mIconStates.get(icon);
579 }
580
Selim Cinek17e1b692016-12-02 18:19:11 -0800581 public void setSpeedBumpIndex(int speedBumpIndex) {
582 mSpeedBumpIndex = speedBumpIndex;
583 }
584
585 public void setOpenedAmount(float expandAmount) {
586 mOpenedAmount = expandAmount;
587 }
588
Selim Cinek932005d2016-12-05 17:12:09 -0800589 public boolean hasOverflow() {
Evan Laird8cf0de42018-02-06 18:34:55 -0500590 return mNumDots > 0;
Selim Cinek932005d2016-12-05 17:12:09 -0800591 }
592
Evan Lairdc987fc72017-12-15 10:14:22 -0500593 /**
594 * If the overflow is in the range [1, max_dots - 1) (basically 1 or 2 dots), then
595 * extra padding will have to be accounted for
596 *
597 * This method has no meaning for non-static containers
598 */
599 public boolean hasPartialOverflow() {
Evan Laird8cf0de42018-02-06 18:34:55 -0500600 return mNumDots > 0 && mNumDots < MAX_DOTS;
Evan Lairdc987fc72017-12-15 10:14:22 -0500601 }
602
603 /**
604 * Get padding that can account for extra dots up to the max. The only valid values for
605 * this method are for 1 or 2 dots.
606 * @return only extraDotPadding or extraDotPadding * 2
607 */
608 public int getPartialOverflowExtraPadding() {
609 if (!hasPartialOverflow()) {
610 return 0;
611 }
612
Evan Laird43c09a92018-03-06 09:46:49 -0500613 int partialOverflowAmount = (MAX_DOTS - mNumDots) * (mStaticDotDiameter + mDotPadding);
Evan Laird8cf0de42018-02-06 18:34:55 -0500614
615 int adjustedWidth = getFinalTranslationX() + partialOverflowAmount;
616 // In case we actually give too much padding...
617 if (adjustedWidth > getWidth()) {
618 partialOverflowAmount = getWidth() - getFinalTranslationX();
619 }
620
621 return partialOverflowAmount;
622 }
623
624 // Give some extra room for btw notifications if we can
625 public int getNoOverflowExtraPadding() {
626 if (mNumDots != 0) {
627 return 0;
628 }
629
Evan Laird6f9e8562018-05-22 19:14:50 -0400630 int collapsedPadding = mOverflowWidth;
Evan Laird8cf0de42018-02-06 18:34:55 -0500631
632 if (collapsedPadding + getFinalTranslationX() > getWidth()) {
633 collapsedPadding = getWidth() - getFinalTranslationX();
634 }
635
636 return collapsedPadding;
Evan Lairdc987fc72017-12-15 10:14:22 -0500637 }
638
Selim Cinek932005d2016-12-05 17:12:09 -0800639 public int getIconSize() {
640 return mIconSize;
641 }
642
Selim Cinek09bd29d2017-02-03 15:30:28 -0800643 public void setAnimationsEnabled(boolean enabled) {
644 if (!enabled && mAnimationsEnabled) {
645 for (int i = 0; i < getChildCount(); i++) {
646 View child = getChildAt(i);
647 ViewState childState = mIconStates.get(child);
648 if (childState != null) {
649 childState.cancelAnimations(child);
650 childState.applyToView(child);
651 }
652 }
653 }
654 mAnimationsEnabled = enabled;
655 }
656
Selim Cinek72fc8db2017-06-06 18:07:47 -0700657 public void setReplacingIcons(ArrayMap<String, ArrayList<StatusBarIcon>> replacingIcons) {
658 mReplacingIcons = replacingIcons;
659 }
660
Selim Cinek332c23f2018-03-16 17:37:50 -0700661 public void showIconIsolated(StatusBarIconView icon, boolean animated) {
Selim Cinekd03518c2018-03-15 12:13:51 -0700662 if (animated) {
Selim Cinek332c23f2018-03-16 17:37:50 -0700663 mIsolatedIconForAnimation = icon != null ? icon : mIsolatedIcon;
Selim Cinekd03518c2018-03-15 12:13:51 -0700664 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800665 mIsolatedIcon = icon;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800666 updateState();
667 }
668
Selim Cinek332c23f2018-03-16 17:37:50 -0700669 public void setIsolatedIconLocation(Rect isolatedIconLocation, boolean requireUpdate) {
670 mIsolatedIconLocation = isolatedIconLocation;
671 if (requireUpdate) {
672 updateState();
673 }
674 }
675
Selim Cinek195dfc52019-05-30 19:35:05 -0700676 public void setOnLockScreen(boolean onLockScreen) {
677 mOnLockScreen = onLockScreen;
678 }
679
Selim Cinek5b5beb012016-11-08 18:11:58 -0800680 public class IconState extends ViewState {
Selim Cinek1f624952017-06-08 19:11:50 -0700681 public static final int NO_VALUE = NotificationIconContainer.NO_VALUE;
Selim Cinekc383fd02016-10-21 15:31:26 -0700682 public float iconAppearAmount = 1.0f;
Selim Cinek2b549f42016-11-22 16:38:51 -0800683 public float clampedAppearAmount = 1.0f;
Selim Cinek49014f82016-11-04 14:55:30 -0700684 public int visibleState;
Selim Cinek5b5beb012016-11-08 18:11:58 -0800685 public boolean justAdded = true;
Selim Cinek72fc8db2017-06-06 18:07:47 -0700686 private boolean justReplaced;
Selim Cinek2b549f42016-11-22 16:38:51 -0800687 public boolean needsCannedAnimation;
Selim Cinek2b549f42016-11-22 16:38:51 -0800688 public boolean useFullTransitionAmount;
Selim Cineka1d97902016-12-14 16:31:40 -0800689 public boolean useLinearTransitionAmount;
Selim Cinek01a73f92016-12-06 16:13:42 -0800690 public boolean translateContent;
Selim Cinek875ba9b2017-02-13 16:20:17 -0800691 public int iconColor = StatusBarIconView.NO_COLOR;
Selim Cinek44d81a62017-05-08 19:45:40 -0700692 public boolean noAnimations;
Selim Cinek1f624952017-06-08 19:11:50 -0700693 public boolean isLastExpandIcon;
694 public int customTransformHeight = NO_VALUE;
Selim Cinekc383fd02016-10-21 15:31:26 -0700695
696 @Override
697 public void applyToView(View view) {
Selim Cinek49014f82016-11-04 14:55:30 -0700698 if (view instanceof StatusBarIconView) {
699 StatusBarIconView icon = (StatusBarIconView) view;
Selim Cinek2b549f42016-11-22 16:38:51 -0800700 boolean animate = false;
701 AnimationProperties animationProperties = null;
Selim Cinekc7e4cb52019-06-20 15:41:45 -0700702 boolean animationsAllowed = areAnimationsEnabled(icon) && !mDisallowNextAnimation
Selim Cinek44d81a62017-05-08 19:45:40 -0700703 && !noAnimations;
Selim Cinek09bd29d2017-02-03 15:30:28 -0800704 if (animationsAllowed) {
Selim Cinek72fc8db2017-06-06 18:07:47 -0700705 if (justAdded || justReplaced) {
Selim Cinek09bd29d2017-02-03 15:30:28 -0800706 super.applyToView(icon);
Selim Cinek72fc8db2017-06-06 18:07:47 -0700707 if (justAdded && iconAppearAmount != 0.0f) {
Selim Cinek09bd29d2017-02-03 15:30:28 -0800708 icon.setAlpha(0.0f);
709 icon.setVisibleState(StatusBarIconView.STATE_HIDDEN,
710 false /* animate */);
711 animationProperties = ADD_ICON_PROPERTIES;
712 animate = true;
713 }
714 } else if (visibleState != icon.getVisibleState()) {
715 animationProperties = DOT_ANIMATION_PROPERTIES;
Selim Cinekedebced2017-02-03 12:34:16 -0800716 animate = true;
717 }
Selim Cinek09bd29d2017-02-03 15:30:28 -0800718 if (!animate && mAddAnimationStartIndex >= 0
719 && indexOfChild(view) >= mAddAnimationStartIndex
720 && (icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN
Selim Cinek5b5beb012016-11-08 18:11:58 -0800721 || visibleState != StatusBarIconView.STATE_HIDDEN)) {
Selim Cinek09bd29d2017-02-03 15:30:28 -0800722 animationProperties = DOT_ANIMATION_PROPERTIES;
723 animate = true;
Selim Cinek2b549f42016-11-22 16:38:51 -0800724 }
Selim Cinek09bd29d2017-02-03 15:30:28 -0800725 if (needsCannedAnimation) {
Lucas Dupin8274aa92017-09-13 17:24:09 -0700726 AnimationFilter animationFilter = sTempProperties.getAnimationFilter();
Selim Cinek09bd29d2017-02-03 15:30:28 -0800727 animationFilter.reset();
728 animationFilter.combineFilter(
729 ICON_ANIMATION_PROPERTIES.getAnimationFilter());
Lucas Dupin8274aa92017-09-13 17:24:09 -0700730 sTempProperties.resetCustomInterpolators();
731 sTempProperties.combineCustomInterpolators(ICON_ANIMATION_PROPERTIES);
Selim Cinek09bd29d2017-02-03 15:30:28 -0800732 if (animationProperties != null) {
733 animationFilter.combineFilter(animationProperties.getAnimationFilter());
Lucas Dupin8274aa92017-09-13 17:24:09 -0700734 sTempProperties.combineCustomInterpolators(animationProperties);
Selim Cinek09bd29d2017-02-03 15:30:28 -0800735 }
Lucas Dupin8274aa92017-09-13 17:24:09 -0700736 animationProperties = sTempProperties;
Selim Cinek09bd29d2017-02-03 15:30:28 -0800737 animationProperties.setDuration(CANNED_ANIMATION_DURATION);
738 animate = true;
739 mCannedAnimationStartIndex = indexOfChild(view);
740 }
741 if (!animate && mCannedAnimationStartIndex >= 0
742 && indexOfChild(view) > mCannedAnimationStartIndex
743 && (icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN
744 || visibleState != StatusBarIconView.STATE_HIDDEN)) {
Lucas Dupin8274aa92017-09-13 17:24:09 -0700745 AnimationFilter animationFilter = sTempProperties.getAnimationFilter();
Selim Cinek09bd29d2017-02-03 15:30:28 -0800746 animationFilter.reset();
747 animationFilter.animateX();
Lucas Dupin8274aa92017-09-13 17:24:09 -0700748 sTempProperties.resetCustomInterpolators();
749 animationProperties = sTempProperties;
Selim Cinek09bd29d2017-02-03 15:30:28 -0800750 animationProperties.setDuration(CANNED_ANIMATION_DURATION);
751 animate = true;
752 }
Selim Cinekd03518c2018-03-15 12:13:51 -0700753 if (mIsolatedIconForAnimation != null) {
754 if (view == mIsolatedIconForAnimation) {
755 animationProperties = UNISOLATION_PROPERTY;
Selim Cinek332c23f2018-03-16 17:37:50 -0700756 animationProperties.setDelay(
757 mIsolatedIcon != null ? CONTENT_FADE_DELAY : 0);
Selim Cinekd03518c2018-03-15 12:13:51 -0700758 } else {
759 animationProperties = UNISOLATION_PROPERTY_OTHERS;
Selim Cinek332c23f2018-03-16 17:37:50 -0700760 animationProperties.setDelay(
761 mIsolatedIcon == null ? CONTENT_FADE_DELAY : 0);
Selim Cinekd03518c2018-03-15 12:13:51 -0700762 }
763 animate = true;
764 }
Selim Cinek2b549f42016-11-22 16:38:51 -0800765 }
Selim Cinek09bd29d2017-02-03 15:30:28 -0800766 icon.setVisibleState(visibleState, animationsAllowed);
Selim Cinek875ba9b2017-02-13 16:20:17 -0800767 icon.setIconColor(iconColor, needsCannedAnimation && animationsAllowed);
Selim Cinek09bd29d2017-02-03 15:30:28 -0800768 if (animate) {
Selim Cinek5b5beb012016-11-08 18:11:58 -0800769 animateTo(icon, animationProperties);
Selim Cinek49014f82016-11-04 14:55:30 -0700770 } else {
771 super.applyToView(view);
772 }
Selim Cinekd95ca7c2017-07-26 12:20:38 -0700773 boolean inShelf = iconAppearAmount == 1.0f;
774 icon.setIsInShelf(inShelf);
Selim Cinek49014f82016-11-04 14:55:30 -0700775 }
Selim Cinek5b5beb012016-11-08 18:11:58 -0800776 justAdded = false;
Selim Cinek72fc8db2017-06-06 18:07:47 -0700777 justReplaced = false;
Selim Cinek2b549f42016-11-22 16:38:51 -0800778 needsCannedAnimation = false;
779 }
780
Selim Cinek1f624952017-06-08 19:11:50 -0700781 public boolean hasCustomTransformHeight() {
782 return isLastExpandIcon && customTransformHeight != NO_VALUE;
783 }
784
Selim Cinek875ba9b2017-02-13 16:20:17 -0800785 @Override
786 public void initFrom(View view) {
787 super.initFrom(view);
788 if (view instanceof StatusBarIconView) {
789 iconColor = ((StatusBarIconView) view).getStaticDrawableColor();
790 }
791 }
Selim Cinekc383fd02016-10-21 15:31:26 -0700792 }
Selim Cinek281c2022016-10-13 19:14:43 -0700793}