blob: 5a7c5c9b5ebce6ec0f6d29f3eaf71e9d5fe6bc47 [file] [log] [blame]
Evan Laird058c8ae2018-01-12 14:26:10 -05001/*
2 * Copyright (C) 2017 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
Evan Laird058c8ae2018-01-12 14:26:10 -050017package com.android.systemui.statusbar.phone;
18
Evan Laird20b87bf2018-04-12 09:54:11 -040019import static com.android.systemui.statusbar.StatusBarIconView.STATE_DOT;
20import static com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN;
Gus Prevasab336792018-11-14 13:52:20 -050021import static com.android.systemui.statusbar.StatusBarIconView.STATE_ICON;
Evan Laird20b87bf2018-04-12 09:54:11 -040022
Evan Laird058c8ae2018-01-12 14:26:10 -050023import android.annotation.Nullable;
24import android.content.Context;
Evan Laird20b87bf2018-04-12 09:54:11 -040025import android.graphics.Canvas;
26import android.graphics.Color;
27import android.graphics.Paint;
28import android.graphics.Paint.Style;
Evan Laird058c8ae2018-01-12 14:26:10 -050029import android.util.AttributeSet;
Evan Lairde1d13c92018-03-20 16:58:01 -040030import android.util.Log;
Evan Laird058c8ae2018-01-12 14:26:10 -050031import android.view.View;
Gus Prevasab336792018-11-14 13:52:20 -050032
Evan Laird058c8ae2018-01-12 14:26:10 -050033import com.android.keyguard.AlphaOptimizedLinearLayout;
34import com.android.systemui.R;
Evan Lairde1d13c92018-03-20 16:58:01 -040035import com.android.systemui.statusbar.StatusIconDisplayable;
Rohan Shah20790b82018-07-02 17:21:04 -070036import com.android.systemui.statusbar.notification.stack.AnimationFilter;
37import com.android.systemui.statusbar.notification.stack.AnimationProperties;
38import com.android.systemui.statusbar.notification.stack.ViewState;
Gus Prevasab336792018-11-14 13:52:20 -050039
Evan Laird20b87bf2018-04-12 09:54:11 -040040import java.util.ArrayList;
Evan Lairdf435fd22019-02-15 13:35:45 -050041import java.util.List;
Evan Laird058c8ae2018-01-12 14:26:10 -050042
Evan Laird937d9fa2018-02-08 10:12:16 -080043/**
44 * A container for Status bar system icons. Limits the number of system icons and handles overflow
Evan Laird20b87bf2018-04-12 09:54:11 -040045 * similar to {@link NotificationIconContainer}.
Evan Laird937d9fa2018-02-08 10:12:16 -080046 *
Evan Laird20b87bf2018-04-12 09:54:11 -040047 * Children are expected to implement {@link StatusIconDisplayable}
Evan Laird937d9fa2018-02-08 10:12:16 -080048 */
Evan Laird058c8ae2018-01-12 14:26:10 -050049public class StatusIconContainer extends AlphaOptimizedLinearLayout {
50
51 private static final String TAG = "StatusIconContainer";
Evan Lairde0fbc3e2018-02-13 11:41:00 -050052 private static final boolean DEBUG = false;
Evan Laird20b87bf2018-04-12 09:54:11 -040053 private static final boolean DEBUG_OVERFLOW = false;
Evan Laird97ae20c2018-05-10 13:13:27 -040054 // Max 8 status icons including battery
Evan Laird150701d42018-05-04 16:14:00 -040055 private static final int MAX_ICONS = 7;
56 private static final int MAX_DOTS = 1;
Evan Laird058c8ae2018-01-12 14:26:10 -050057
Evan Laird20b87bf2018-04-12 09:54:11 -040058 private int mDotPadding;
JianYang Liueba03472020-01-14 17:28:18 -080059 private int mIconSpacing;
Evan Laird20b87bf2018-04-12 09:54:11 -040060 private int mStaticDotDiameter;
61 private int mUnderflowWidth;
62 private int mUnderflowStart = 0;
63 // Whether or not we can draw into the underflow space
64 private boolean mNeedsUnderflow;
65 // Individual StatusBarIconViews draw their etc dots centered in this width
66 private int mIconDotFrameWidth;
67 private boolean mShouldRestrictIcons = true;
68 // Used to count which states want to be visible during layout
69 private ArrayList<StatusIconState> mLayoutStates = new ArrayList<>();
70 // So we can count and measure properly
71 private ArrayList<View> mMeasureViews = new ArrayList<>();
Evan Lairdf435fd22019-02-15 13:35:45 -050072 // Any ignored icon will never be added as a child
73 private ArrayList<String> mIgnoredSlots = new ArrayList<>();
Evan Laird20b87bf2018-04-12 09:54:11 -040074
Evan Laird937d9fa2018-02-08 10:12:16 -080075 public StatusIconContainer(Context context) {
76 this(context, null);
77 }
78
Evan Laird058c8ae2018-01-12 14:26:10 -050079 public StatusIconContainer(Context context, AttributeSet attrs) {
80 super(context, attrs);
Evan Lairdfeec2ab2018-05-02 12:47:26 -040081 initDimens();
82 setWillNotDraw(!DEBUG_OVERFLOW);
Evan Laird058c8ae2018-01-12 14:26:10 -050083 }
84
85 @Override
Evan Laird20b87bf2018-04-12 09:54:11 -040086 protected void onFinishInflate() {
87 super.onFinishInflate();
Evan Laird20b87bf2018-04-12 09:54:11 -040088 }
89
90 public void setShouldRestrictIcons(boolean should) {
91 mShouldRestrictIcons = should;
92 }
93
Evan Lairdfeec2ab2018-05-02 12:47:26 -040094 public boolean isRestrictingIcons() {
95 return mShouldRestrictIcons;
96 }
97
Evan Laird20b87bf2018-04-12 09:54:11 -040098 private void initDimens() {
99 // This is the same value that StatusBarIconView uses
100 mIconDotFrameWidth = getResources().getDimensionPixelSize(
101 com.android.internal.R.dimen.status_bar_icon_size);
102 mDotPadding = getResources().getDimensionPixelSize(R.dimen.overflow_icon_dot_padding);
JianYang Liueba03472020-01-14 17:28:18 -0800103 mIconSpacing = getResources().getDimensionPixelSize(R.dimen.status_bar_system_icon_spacing);
Evan Laird20b87bf2018-04-12 09:54:11 -0400104 int radius = getResources().getDimensionPixelSize(R.dimen.overflow_dot_radius);
105 mStaticDotDiameter = 2 * radius;
Evan Laird150701d42018-05-04 16:14:00 -0400106 mUnderflowWidth = mIconDotFrameWidth + (MAX_DOTS - 1) * (mStaticDotDiameter + mDotPadding);
Evan Laird20b87bf2018-04-12 09:54:11 -0400107 }
108
109 @Override
Evan Laird058c8ae2018-01-12 14:26:10 -0500110 protected void onLayout(boolean changed, int l, int t, int r, int b) {
111 float midY = getHeight() / 2.0f;
112
113 // Layout all child views so that we can move them around later
114 for (int i = 0; i < getChildCount(); i++) {
115 View child = getChildAt(i);
116 int width = child.getMeasuredWidth();
117 int height = child.getMeasuredHeight();
118 int top = (int) (midY - height / 2.0f);
119 child.layout(0, top, width, top + height);
120 }
121
122 resetViewStates();
123 calculateIconTranslations();
124 applyIconStates();
125 }
126
127 @Override
Evan Laird20b87bf2018-04-12 09:54:11 -0400128 protected void onDraw(Canvas canvas) {
129 super.onDraw(canvas);
130 if (DEBUG_OVERFLOW) {
131 Paint paint = new Paint();
132 paint.setStyle(Style.STROKE);
133 paint.setColor(Color.RED);
134
135 // Show bounding box
136 canvas.drawRect(getPaddingStart(), 0, getWidth() - getPaddingEnd(), getHeight(), paint);
137
138 // Show etc box
139 paint.setColor(Color.GREEN);
140 canvas.drawRect(
141 mUnderflowStart, 0, mUnderflowStart + mUnderflowWidth, getHeight(), paint);
142 }
143 }
144
145 @Override
Evan Laird058c8ae2018-01-12 14:26:10 -0500146 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Evan Laird20b87bf2018-04-12 09:54:11 -0400147 mMeasureViews.clear();
148 int mode = MeasureSpec.getMode(widthMeasureSpec);
149 final int width = MeasureSpec.getSize(widthMeasureSpec);
Evan Laird058c8ae2018-01-12 14:26:10 -0500150 final int count = getChildCount();
Evan Laird20b87bf2018-04-12 09:54:11 -0400151 // Collect all of the views which want to be laid out
Evan Laird058c8ae2018-01-12 14:26:10 -0500152 for (int i = 0; i < count; i++) {
Evan Laird20b87bf2018-04-12 09:54:11 -0400153 StatusIconDisplayable icon = (StatusIconDisplayable) getChildAt(i);
Evan Lairdf435fd22019-02-15 13:35:45 -0500154 if (icon.isIconVisible() && !icon.isIconBlocked()
155 && !mIgnoredSlots.contains(icon.getSlot())) {
Evan Laird20b87bf2018-04-12 09:54:11 -0400156 mMeasureViews.add((View) icon);
157 }
158 }
159
160 int visibleCount = mMeasureViews.size();
161 int maxVisible = visibleCount <= MAX_ICONS ? MAX_ICONS : MAX_ICONS - 1;
Evan Laird97ae20c2018-05-10 13:13:27 -0400162 int totalWidth = mPaddingLeft + mPaddingRight;
Evan Laird20b87bf2018-04-12 09:54:11 -0400163 boolean trackWidth = true;
164
165 // Measure all children so that they report the correct width
166 int childWidthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.UNSPECIFIED);
167 mNeedsUnderflow = mShouldRestrictIcons && visibleCount > MAX_ICONS;
JianYang Liueba03472020-01-14 17:28:18 -0800168 for (int i = 0; i < visibleCount; i++) {
Evan Laird20b87bf2018-04-12 09:54:11 -0400169 // Walking backwards
170 View child = mMeasureViews.get(visibleCount - i - 1);
171 measureChild(child, childWidthSpec, heightMeasureSpec);
JianYang Liueba03472020-01-14 17:28:18 -0800172 int spacing = i == visibleCount - 1 ? 0 : mIconSpacing;
Evan Laird20b87bf2018-04-12 09:54:11 -0400173 if (mShouldRestrictIcons) {
174 if (i < maxVisible && trackWidth) {
JianYang Liueba03472020-01-14 17:28:18 -0800175 totalWidth += getViewTotalMeasuredWidth(child) + spacing;
Evan Laird20b87bf2018-04-12 09:54:11 -0400176 } else if (trackWidth) {
177 // We've hit the icon limit; add space for dots
178 totalWidth += mUnderflowWidth;
179 trackWidth = false;
180 }
181 } else {
JianYang Liueba03472020-01-14 17:28:18 -0800182 totalWidth += getViewTotalMeasuredWidth(child) + spacing;
Evan Laird20b87bf2018-04-12 09:54:11 -0400183 }
184 }
185
186 if (mode == MeasureSpec.EXACTLY) {
187 if (!mNeedsUnderflow && totalWidth > width) {
188 mNeedsUnderflow = true;
189 }
190 setMeasuredDimension(width, MeasureSpec.getSize(heightMeasureSpec));
191 } else {
192 if (mode == MeasureSpec.AT_MOST && totalWidth > width) {
193 mNeedsUnderflow = true;
194 totalWidth = width;
195 }
196 setMeasuredDimension(totalWidth, MeasureSpec.getSize(heightMeasureSpec));
Evan Laird058c8ae2018-01-12 14:26:10 -0500197 }
198 }
199
200 @Override
201 public void onViewAdded(View child) {
202 super.onViewAdded(child);
Evan Laird20b87bf2018-04-12 09:54:11 -0400203 StatusIconState vs = new StatusIconState();
Evan Laird150701d42018-05-04 16:14:00 -0400204 vs.justAdded = true;
Evan Laird058c8ae2018-01-12 14:26:10 -0500205 child.setTag(R.id.status_bar_view_state_tag, vs);
206 }
207
208 @Override
209 public void onViewRemoved(View child) {
210 super.onViewRemoved(child);
211 child.setTag(R.id.status_bar_view_state_tag, null);
212 }
213
214 /**
Evan Lairdf435fd22019-02-15 13:35:45 -0500215 * Add a name of an icon slot to be ignored. It will not show up nor be measured
216 * @param slotName name of the icon as it exists in
217 * frameworks/base/core/res/res/values/config.xml
218 */
219 public void addIgnoredSlot(String slotName) {
220 addIgnoredSlotInternal(slotName);
221 requestLayout();
222 }
223
224 /**
225 * Add a list of slots to be ignored
226 * @param slots names of the icons to ignore
227 */
228 public void addIgnoredSlots(List<String> slots) {
229 for (String slot : slots) {
230 addIgnoredSlotInternal(slot);
231 }
232
233 requestLayout();
234 }
235
236 private void addIgnoredSlotInternal(String slotName) {
237 if (!mIgnoredSlots.contains(slotName)) {
238 mIgnoredSlots.add(slotName);
239 }
240 }
241
242 /**
243 * Remove a slot from the list of ignored icon slots. It will then be shown when set to visible
244 * by the {@link StatusBarIconController}.
245 * @param slotName name of the icon slot to remove from the ignored list
246 */
247 public void removeIgnoredSlot(String slotName) {
248 if (mIgnoredSlots.contains(slotName)) {
249 mIgnoredSlots.remove(slotName);
250 }
251
252 requestLayout();
253 }
254
255 /**
Fabian Kozynski16b26992019-05-06 10:18:41 -0400256 * Sets the list of ignored icon slots clearing the current list.
257 * @param slots names of the icons to ignore
258 */
259 public void setIgnoredSlots(List<String> slots) {
260 mIgnoredSlots.clear();
261 addIgnoredSlots(slots);
262 }
263
264 /**
Evan Laird058c8ae2018-01-12 14:26:10 -0500265 * Layout is happening from end -> start
266 */
267 private void calculateIconTranslations() {
Evan Laird20b87bf2018-04-12 09:54:11 -0400268 mLayoutStates.clear();
Evan Laird97ae20c2018-05-10 13:13:27 -0400269 float width = getWidth();
270 float translationX = width - getPaddingEnd();
Evan Laird058c8ae2018-01-12 14:26:10 -0500271 float contentStart = getPaddingStart();
272 int childCount = getChildCount();
273 // Underflow === don't show content until that index
Evan Lairdfeec2ab2018-05-02 12:47:26 -0400274 if (DEBUG) android.util.Log.d(TAG, "calculateIconTranslations: start=" + translationX
275 + " width=" + width + " underflow=" + mNeedsUnderflow);
Evan Laird058c8ae2018-01-12 14:26:10 -0500276
Evan Laird20b87bf2018-04-12 09:54:11 -0400277 // Collect all of the states which want to be visible
Evan Laird058c8ae2018-01-12 14:26:10 -0500278 for (int i = childCount - 1; i >= 0; i--) {
279 View child = getChildAt(i);
Evan Lairde1d13c92018-03-20 16:58:01 -0400280 StatusIconDisplayable iconView = (StatusIconDisplayable) child;
Evan Laird20b87bf2018-04-12 09:54:11 -0400281 StatusIconState childState = getViewStateFromChild(child);
Evan Laird058c8ae2018-01-12 14:26:10 -0500282
Evan Lairdf435fd22019-02-15 13:35:45 -0500283 if (!iconView.isIconVisible() || iconView.isIconBlocked()
284 || mIgnoredSlots.contains(iconView.getSlot())) {
Evan Laird20b87bf2018-04-12 09:54:11 -0400285 childState.visibleState = STATE_HIDDEN;
Evan Lairde1d13c92018-03-20 16:58:01 -0400286 if (DEBUG) Log.d(TAG, "skipping child (" + iconView.getSlot() + ") not visible");
Evan Laird058c8ae2018-01-12 14:26:10 -0500287 continue;
288 }
289
JianYang Liueba03472020-01-14 17:28:18 -0800290 // Move translationX to the spot within StatusIconContainer's layout to add the view
291 // without cutting off the child view.
292 translationX -= getViewTotalWidth(child);
Evan Laird20b87bf2018-04-12 09:54:11 -0400293 childState.visibleState = STATE_ICON;
JianYang Liueba03472020-01-14 17:28:18 -0800294 childState.xTranslation = translationX;
Evan Laird20b87bf2018-04-12 09:54:11 -0400295 mLayoutStates.add(0, childState);
Evan Laird058c8ae2018-01-12 14:26:10 -0500296
JianYang Liueba03472020-01-14 17:28:18 -0800297 // Shift translationX over by mIconSpacing for the next view.
298 translationX -= mIconSpacing;
Evan Laird20b87bf2018-04-12 09:54:11 -0400299 }
300
Evan Laird150701d42018-05-04 16:14:00 -0400301 // Show either 1-MAX_ICONS icons, or (MAX_ICONS - 1) icons + overflow
Evan Laird20b87bf2018-04-12 09:54:11 -0400302 int totalVisible = mLayoutStates.size();
303 int maxVisible = totalVisible <= MAX_ICONS ? MAX_ICONS : MAX_ICONS - 1;
304
305 mUnderflowStart = 0;
306 int visible = 0;
Evan Laird150701d42018-05-04 16:14:00 -0400307 int firstUnderflowIndex = -1;
Evan Laird20b87bf2018-04-12 09:54:11 -0400308 for (int i = totalVisible - 1; i >= 0; i--) {
309 StatusIconState state = mLayoutStates.get(i);
310 // Allow room for underflow if we found we need it in onMeasure
311 if (mNeedsUnderflow && (state.xTranslation < (contentStart + mUnderflowWidth))||
312 (mShouldRestrictIcons && visible >= maxVisible)) {
313 firstUnderflowIndex = i;
314 break;
Evan Laird058c8ae2018-01-12 14:26:10 -0500315 }
JianYang Liueba03472020-01-14 17:28:18 -0800316 mUnderflowStart = (int) Math.max(
317 contentStart, state.xTranslation - mUnderflowWidth - mIconSpacing);
Evan Laird20b87bf2018-04-12 09:54:11 -0400318 visible++;
Evan Laird058c8ae2018-01-12 14:26:10 -0500319 }
320
321 if (firstUnderflowIndex != -1) {
Evan Laird20b87bf2018-04-12 09:54:11 -0400322 int totalDots = 0;
323 int dotWidth = mStaticDotDiameter + mDotPadding;
324 int dotOffset = mUnderflowStart + mUnderflowWidth - mIconDotFrameWidth;
325 for (int i = firstUnderflowIndex; i >= 0; i--) {
326 StatusIconState state = mLayoutStates.get(i);
327 if (totalDots < MAX_DOTS) {
328 state.xTranslation = dotOffset;
329 state.visibleState = STATE_DOT;
330 dotOffset -= dotWidth;
331 totalDots++;
332 } else {
333 state.visibleState = STATE_HIDDEN;
Evan Laird058c8ae2018-01-12 14:26:10 -0500334 }
335 }
336 }
Evan Laird63036132018-02-02 16:30:41 -0500337
338 // Stole this from NotificationIconContainer. Not optimal but keeps the layout logic clean
339 if (isLayoutRtl()) {
340 for (int i = 0; i < childCount; i++) {
341 View child = getChildAt(i);
Evan Laird20b87bf2018-04-12 09:54:11 -0400342 StatusIconState state = getViewStateFromChild(child);
Evan Laird63036132018-02-02 16:30:41 -0500343 state.xTranslation = width - state.xTranslation - child.getWidth();
344 }
345 }
Evan Laird058c8ae2018-01-12 14:26:10 -0500346 }
347
348 private void applyIconStates() {
349 for (int i = 0; i < getChildCount(); i++) {
350 View child = getChildAt(i);
Evan Laird20b87bf2018-04-12 09:54:11 -0400351 StatusIconState vs = getViewStateFromChild(child);
Evan Laird058c8ae2018-01-12 14:26:10 -0500352 if (vs != null) {
353 vs.applyToView(child);
354 }
355 }
356 }
357
358 private void resetViewStates() {
359 for (int i = 0; i < getChildCount(); i++) {
360 View child = getChildAt(i);
Evan Laird20b87bf2018-04-12 09:54:11 -0400361 StatusIconState vs = getViewStateFromChild(child);
Evan Laird058c8ae2018-01-12 14:26:10 -0500362 if (vs == null) {
363 continue;
364 }
365
366 vs.initFrom(child);
367 vs.alpha = 1.0f;
Evan Laird620ea2c2018-06-21 11:18:19 -0400368 vs.hidden = false;
Evan Laird058c8ae2018-01-12 14:26:10 -0500369 }
370 }
371
Evan Laird20b87bf2018-04-12 09:54:11 -0400372 private static @Nullable StatusIconState getViewStateFromChild(View child) {
373 return (StatusIconState) child.getTag(R.id.status_bar_view_state_tag);
374 }
375
376 private static int getViewTotalMeasuredWidth(View child) {
377 return child.getMeasuredWidth() + child.getPaddingStart() + child.getPaddingEnd();
378 }
379
380 private static int getViewTotalWidth(View child) {
381 return child.getWidth() + child.getPaddingStart() + child.getPaddingEnd();
382 }
383
384 public static class StatusIconState extends ViewState {
385 /// StatusBarIconView.STATE_*
386 public int visibleState = STATE_ICON;
Evan Laird150701d42018-05-04 16:14:00 -0400387 public boolean justAdded = true;
Evan Laird20b87bf2018-04-12 09:54:11 -0400388
Evan Laird798d1f82019-05-10 16:56:29 -0400389 // How far we are from the end of the view actually is the most relevant for animation
390 float distanceToViewEnd = -1;
391
Evan Laird20b87bf2018-04-12 09:54:11 -0400392 @Override
393 public void applyToView(View view) {
Evan Laird798d1f82019-05-10 16:56:29 -0400394 float parentWidth = 0;
395 if (view.getParent() instanceof View) {
396 parentWidth = ((View) view.getParent()).getWidth();
397 }
398
399 float currentDistanceToEnd = parentWidth - xTranslation;
400
Evan Laird150701d42018-05-04 16:14:00 -0400401 if (!(view instanceof StatusIconDisplayable)) {
402 return;
Evan Laird20b87bf2018-04-12 09:54:11 -0400403 }
Evan Laird150701d42018-05-04 16:14:00 -0400404 StatusIconDisplayable icon = (StatusIconDisplayable) view;
405 AnimationProperties animationProperties = null;
Evan Laird620ea2c2018-06-21 11:18:19 -0400406 boolean animateVisibility = true;
Evan Laird150701d42018-05-04 16:14:00 -0400407
Evan Laird620ea2c2018-06-21 11:18:19 -0400408 // Figure out which properties of the state transition (if any) we need to animate
409 if (justAdded
410 || icon.getVisibleState() == STATE_HIDDEN && visibleState == STATE_ICON) {
411 // Icon is appearing, fade it in by putting it where it will be and animating alpha
Evan Laird150701d42018-05-04 16:14:00 -0400412 super.applyToView(view);
Evan Laird620ea2c2018-06-21 11:18:19 -0400413 view.setAlpha(0.f);
414 icon.setVisibleState(STATE_HIDDEN);
Evan Laird150701d42018-05-04 16:14:00 -0400415 animationProperties = ADD_ICON_PROPERTIES;
Evan Laird150701d42018-05-04 16:14:00 -0400416 } else if (icon.getVisibleState() != visibleState) {
Evan Laird620ea2c2018-06-21 11:18:19 -0400417 if (icon.getVisibleState() == STATE_ICON && visibleState == STATE_HIDDEN) {
418 // Disappearing, don't do anything fancy
419 animateVisibility = false;
420 } else {
421 // all other transitions (to/from dot, etc)
422 animationProperties = ANIMATE_ALL_PROPERTIES;
423 }
Evan Laird798d1f82019-05-10 16:56:29 -0400424 } else if (visibleState != STATE_HIDDEN && distanceToViewEnd != currentDistanceToEnd) {
Evan Laird620ea2c2018-06-21 11:18:19 -0400425 // Visibility isn't changing, just animate position
426 animationProperties = X_ANIMATION_PROPERTIES;
Evan Laird150701d42018-05-04 16:14:00 -0400427 }
428
Evan Laird620ea2c2018-06-21 11:18:19 -0400429 icon.setVisibleState(visibleState, animateVisibility);
430 if (animationProperties != null) {
Evan Laird150701d42018-05-04 16:14:00 -0400431 animateTo(view, animationProperties);
432 } else {
433 super.applyToView(view);
434 }
435
436 justAdded = false;
Evan Laird798d1f82019-05-10 16:56:29 -0400437 distanceToViewEnd = currentDistanceToEnd;
438
Evan Laird20b87bf2018-04-12 09:54:11 -0400439 }
Evan Laird058c8ae2018-01-12 14:26:10 -0500440 }
Evan Laird150701d42018-05-04 16:14:00 -0400441
442 private static final AnimationProperties ADD_ICON_PROPERTIES = new AnimationProperties() {
443 private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha();
444
445 @Override
446 public AnimationFilter getAnimationFilter() {
447 return mAnimationFilter;
448 }
449 }.setDuration(200).setDelay(50);
450
Evan Laird620ea2c2018-06-21 11:18:19 -0400451 private static final AnimationProperties X_ANIMATION_PROPERTIES = new AnimationProperties() {
Evan Laird150701d42018-05-04 16:14:00 -0400452 private AnimationFilter mAnimationFilter = new AnimationFilter().animateX();
453
454 @Override
455 public AnimationFilter getAnimationFilter() {
456 return mAnimationFilter;
457 }
458 }.setDuration(200);
Evan Laird620ea2c2018-06-21 11:18:19 -0400459
460 private static final AnimationProperties ANIMATE_ALL_PROPERTIES = new AnimationProperties() {
461 private AnimationFilter mAnimationFilter = new AnimationFilter().animateX().animateY()
462 .animateAlpha().animateScale();
463
464 @Override
465 public AnimationFilter getAnimationFilter() {
466 return mAnimationFilter;
467 }
468 }.setDuration(200);
Evan Laird058c8ae2018-01-12 14:26:10 -0500469}