StatusBarHeaderView.java revision 31ba192dd201df2cad96a8c503f730130ab0d80f
1/*
2 * Copyright (C) 2014 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
19import android.content.Context;
20import android.content.Intent;
21import android.graphics.Outline;
22import android.graphics.Rect;
23import android.graphics.drawable.Drawable;
24import android.util.AttributeSet;
25import android.view.View;
26import android.view.ViewGroup;
27import android.view.ViewOutlineProvider;
28import android.widget.ImageView;
29import android.widget.LinearLayout;
30import android.widget.RelativeLayout;
31import android.widget.Switch;
32import android.widget.TextView;
33
34import com.android.systemui.R;
35import com.android.systemui.qs.QSPanel;
36import com.android.systemui.qs.QSTile;
37import com.android.systemui.statusbar.policy.BatteryController;
38import com.android.systemui.statusbar.policy.UserInfoController;
39
40/**
41 * The view to manage the header area in the expanded status bar.
42 */
43public class StatusBarHeaderView extends RelativeLayout implements View.OnClickListener,
44        BatteryController.BatteryStateChangeCallback {
45
46    private boolean mExpanded;
47    private boolean mListening;
48    private boolean mOverscrolled;
49    private boolean mKeyguardShowing;
50    private boolean mCharging;
51
52    private ViewGroup mSystemIconsContainer;
53    private View mSystemIconsSuperContainer;
54    private View mDateTime;
55    private View mTime;
56    private View mAmPm;
57    private View mKeyguardCarrierText;
58    private MultiUserSwitch mMultiUserSwitch;
59    private ImageView mMultiUserAvatar;
60    private View mDateCollapsed;
61    private View mDateExpanded;
62    private View mStatusIcons;
63    private View mSignalCluster;
64    private View mSettingsButton;
65    private View mQsDetailHeader;
66    private TextView mQsDetailHeaderTitle;
67    private Switch mQsDetailHeaderSwitch;
68    private View mEmergencyCallsOnly;
69    private TextView mBatteryLevel;
70
71    private boolean mShowEmergencyCallsOnly;
72    private boolean mKeyguardUserSwitcherShowing;
73
74    private int mCollapsedHeight;
75    private int mExpandedHeight;
76    private int mKeyguardHeight;
77
78    private int mKeyguardWidth = ViewGroup.LayoutParams.MATCH_PARENT;
79    private int mNormalWidth;
80    private int mPadding;
81    private int mMultiUserExpandedMargin;
82    private int mMultiUserCollapsedMargin;
83    private int mMultiUserKeyguardMargin;
84    private int mSystemIconsSwitcherHiddenExpandedMargin;
85    private int mClockMarginBottomExpanded;
86    private int mMultiUserSwitchWidthCollapsed;
87    private int mMultiUserSwitchWidthExpanded;
88    private int mBatteryPaddingEnd;
89
90    /**
91     * In collapsed QS, the clock and avatar are scaled down a bit post-layout to allow for a nice
92     * transition. These values determine that factor.
93     */
94    private float mClockCollapsedScaleFactor;
95    private float mAvatarCollapsedScaleFactor;
96
97    private ActivityStarter mActivityStarter;
98    private BatteryController mBatteryController;
99    private QSPanel mQSPanel;
100
101    private final Rect mClipBounds = new Rect();
102
103    public StatusBarHeaderView(Context context, AttributeSet attrs) {
104        super(context, attrs);
105    }
106
107    @Override
108    protected void onFinishInflate() {
109        super.onFinishInflate();
110        mSystemIconsSuperContainer = findViewById(R.id.system_icons_super_container);
111        mSystemIconsContainer = (ViewGroup) findViewById(R.id.system_icons_container);
112        mSystemIconsSuperContainer.setOnClickListener(this);
113        mDateTime = findViewById(R.id.datetime);
114        mTime = findViewById(R.id.time_view);
115        mAmPm = findViewById(R.id.am_pm_view);
116        mKeyguardCarrierText = findViewById(R.id.keyguard_carrier_text);
117        mMultiUserSwitch = (MultiUserSwitch) findViewById(R.id.multi_user_switch);
118        mMultiUserAvatar = (ImageView) findViewById(R.id.multi_user_avatar);
119        mDateCollapsed = findViewById(R.id.date_collapsed);
120        mDateExpanded = findViewById(R.id.date_expanded);
121        mSettingsButton = findViewById(R.id.settings_button);
122        mSettingsButton.setOnClickListener(this);
123        mQsDetailHeader = findViewById(R.id.qs_detail_header);
124        mQsDetailHeader.setAlpha(0);
125        mQsDetailHeaderTitle = (TextView) mQsDetailHeader.findViewById(android.R.id.title);
126        mQsDetailHeaderSwitch = (Switch) mQsDetailHeader.findViewById(android.R.id.toggle);
127        mEmergencyCallsOnly = findViewById(R.id.header_emergency_calls_only);
128        mBatteryLevel = (TextView) findViewById(R.id.battery_level);
129        loadDimens();
130        updateVisibilities();
131        updateClockScale();
132        updateAvatarScale();
133        addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
134            @Override
135            public void onLayoutChange(View v, int left, int top, int right,
136                    int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
137                if ((right - left) != (oldRight - oldLeft)) {
138                    // width changed, update clipping
139                    setClipping(getHeight());
140                }
141                boolean rtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
142                mTime.setPivotX(rtl ? mTime.getWidth() : 0);
143                mTime.setPivotY(mTime.getBaseline());
144                mAmPm.setPivotX(rtl ? mAmPm.getWidth() : 0);
145                mAmPm.setPivotY(mAmPm.getBaseline());
146                updateAmPmTranslation();
147            }
148        });
149        setOutlineProvider(new ViewOutlineProvider() {
150            @Override
151            public void getOutline(View view, Outline outline) {
152                outline.setRect(mClipBounds);
153            }
154        });
155    }
156
157    private void loadDimens() {
158        mCollapsedHeight = getResources().getDimensionPixelSize(R.dimen.status_bar_header_height);
159        mExpandedHeight = getResources().getDimensionPixelSize(
160                R.dimen.status_bar_header_height_expanded);
161        mKeyguardHeight = getResources().getDimensionPixelSize(
162                R.dimen.status_bar_header_height_keyguard);
163        mNormalWidth = getLayoutParams().width;
164        mPadding = getResources().getDimensionPixelSize(R.dimen.notification_side_padding);
165        mMultiUserExpandedMargin =
166                getResources().getDimensionPixelSize(R.dimen.multi_user_switch_expanded_margin);
167        mMultiUserCollapsedMargin =
168                getResources().getDimensionPixelSize(R.dimen.multi_user_switch_collapsed_margin);
169        mMultiUserKeyguardMargin =
170                getResources().getDimensionPixelSize(R.dimen.multi_user_switch_keyguard_margin);
171        mSystemIconsSwitcherHiddenExpandedMargin = getResources().getDimensionPixelSize(
172                R.dimen.system_icons_switcher_hidden_expanded_margin);
173        mClockMarginBottomExpanded =
174                getResources().getDimensionPixelSize(R.dimen.clock_expanded_bottom_margin);
175        mMultiUserSwitchWidthCollapsed =
176                getResources().getDimensionPixelSize(R.dimen.multi_user_switch_width_collapsed);
177        mMultiUserSwitchWidthExpanded =
178                getResources().getDimensionPixelSize(R.dimen.multi_user_switch_width_expanded);
179        mAvatarCollapsedScaleFactor =
180                getResources().getDimensionPixelSize(R.dimen.multi_user_avatar_collapsed_size)
181                / (float) mMultiUserAvatar.getLayoutParams().width;
182        mClockCollapsedScaleFactor =
183                (float) getResources().getDimensionPixelSize(R.dimen.qs_time_collapsed_size)
184                / (float) getResources().getDimensionPixelSize(R.dimen.qs_time_expanded_size);
185        mBatteryPaddingEnd =
186                getResources().getDimensionPixelSize(R.dimen.battery_level_padding_end);
187    }
188
189    public void setActivityStarter(ActivityStarter activityStarter) {
190        mActivityStarter = activityStarter;
191    }
192
193    public void setBatteryController(BatteryController batteryController) {
194        mBatteryController = batteryController;
195    }
196
197    public int getCollapsedHeight() {
198        return mKeyguardShowing ? mKeyguardHeight : mCollapsedHeight;
199    }
200
201    public int getExpandedHeight() {
202        return mExpandedHeight;
203    }
204
205    public void setListening(boolean listening) {
206        if (listening == mListening) {
207            return;
208        }
209        mListening = listening;
210        updateBatteryListening();
211    }
212
213    public void setExpanded(boolean expanded, boolean overscrolled) {
214        boolean changed = expanded != mExpanded;
215        boolean overscrollChanged = overscrolled != mOverscrolled;
216        mExpanded = expanded;
217        mOverscrolled = overscrolled;
218        if (changed || overscrollChanged) {
219            updateHeights();
220            updateVisibilities();
221            updateSystemIconsLayoutParams();
222            updateZTranslation();
223            updateClickTargets();
224            updateWidth();
225            updatePadding();
226            updateMultiUserSwitch();
227            if (mQSPanel != null) {
228                mQSPanel.setExpanded(expanded && !overscrolled);
229            }
230            updateClockScale();
231            updateAvatarScale();
232            updateClockLp();
233            updateBatteryLevelPaddingEnd();
234        }
235    }
236
237    private void updateHeights() {
238        boolean onKeyguardAndCollapsed = mKeyguardShowing && !mExpanded;
239        int height;
240        if (mExpanded) {
241            height = mExpandedHeight;
242        } else if (onKeyguardAndCollapsed) {
243            height = mKeyguardHeight;
244        } else {
245            height = mCollapsedHeight;
246        }
247        ViewGroup.LayoutParams lp = getLayoutParams();
248        if (lp.height != height) {
249            lp.height = height;
250            setLayoutParams(lp);
251        }
252        int systemIconsContainerHeight = onKeyguardAndCollapsed ? mKeyguardHeight : mCollapsedHeight;
253        lp = mSystemIconsSuperContainer.getLayoutParams();
254        if (lp.height != systemIconsContainerHeight) {
255            lp.height = systemIconsContainerHeight;
256            mSystemIconsSuperContainer.setLayoutParams(lp);
257        }
258        lp = mMultiUserSwitch.getLayoutParams();
259        if (lp.height != systemIconsContainerHeight) {
260            lp.height = systemIconsContainerHeight;
261            mMultiUserSwitch.setLayoutParams(lp);
262        }
263    }
264
265    private void updateWidth() {
266        int width = (mKeyguardShowing && !mExpanded) ? mKeyguardWidth : mNormalWidth;
267        ViewGroup.LayoutParams lp = getLayoutParams();
268        if (width != lp.width) {
269            lp.width = width;
270            setLayoutParams(lp);
271        }
272    }
273
274    private void updateVisibilities() {
275        boolean onKeyguardAndCollapsed = mKeyguardShowing && !mExpanded;
276        if (onKeyguardAndCollapsed) {
277            setBackground(null);
278        } else {
279            setBackgroundResource(R.drawable.notification_header_bg);
280        }
281        mDateTime.setVisibility(onKeyguardAndCollapsed ? View.INVISIBLE : View.VISIBLE);
282        mKeyguardCarrierText.setVisibility(onKeyguardAndCollapsed ? View.VISIBLE : View.GONE);
283        mDateCollapsed.setVisibility(mExpanded && !mOverscrolled ? View.GONE : View.VISIBLE);
284        mDateExpanded.setVisibility(mExpanded && !mOverscrolled ? View.VISIBLE : View.GONE);
285        mSettingsButton.setVisibility(mExpanded && !mOverscrolled ? View.VISIBLE : View.GONE);
286        mQsDetailHeader.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
287        if (mStatusIcons != null) {
288            mStatusIcons.setVisibility(!mExpanded || mOverscrolled ? View.VISIBLE : View.GONE);
289        }
290        if (mSignalCluster != null) {
291            mSignalCluster.setVisibility(!mExpanded || mOverscrolled ? View.VISIBLE : View.GONE);
292        }
293        mEmergencyCallsOnly.setVisibility(mExpanded && !mOverscrolled && mShowEmergencyCallsOnly
294                ? VISIBLE : GONE);
295        mMultiUserSwitch.setVisibility(mExpanded || !mKeyguardUserSwitcherShowing
296                ? VISIBLE : GONE);
297        mBatteryLevel.setVisibility(mKeyguardShowing && mCharging || mExpanded && !mOverscrolled
298                ? View.VISIBLE : View.GONE);
299    }
300
301    private void updateSystemIconsLayoutParams() {
302        RelativeLayout.LayoutParams lp = (LayoutParams) mSystemIconsSuperContainer.getLayoutParams();
303        lp.addRule(RelativeLayout.START_OF, mExpanded && !mOverscrolled
304                ? mSettingsButton.getId()
305                : mMultiUserSwitch.getId());
306        lp.removeRule(ALIGN_PARENT_START);
307        if (mMultiUserSwitch.getVisibility() == GONE) {
308            lp.setMarginEnd(mSystemIconsSwitcherHiddenExpandedMargin);
309        } else {
310            lp.setMarginEnd(0);
311        }
312        mSystemIconsSuperContainer.setLayoutParams(lp);
313    }
314
315    private void updateBatteryListening() {
316        if (mListening) {
317            mBatteryController.addStateChangedCallback(this);
318        } else {
319            mBatteryController.removeStateChangedCallback(this);
320        }
321    }
322
323    private void updateAvatarScale() {
324        if (!mExpanded || mOverscrolled) {
325            mMultiUserSwitch.setScaleX(mAvatarCollapsedScaleFactor);
326            mMultiUserSwitch.setScaleY(mAvatarCollapsedScaleFactor);
327        } else {
328            mMultiUserSwitch.setScaleX(1f);
329            mMultiUserSwitch.setScaleY(1f);
330        }
331    }
332
333    private void updateClockScale() {
334        mAmPm.setScaleX(mClockCollapsedScaleFactor);
335        mAmPm.setScaleY(mClockCollapsedScaleFactor);
336        if (!mExpanded || mOverscrolled) {
337            mTime.setScaleX(mClockCollapsedScaleFactor);
338            mTime.setScaleY(mClockCollapsedScaleFactor);
339        } else {
340            mTime.setScaleX(1f);
341            mTime.setScaleY(1f);
342        }
343        updateAmPmTranslation();
344    }
345
346    private void updateAmPmTranslation() {
347        boolean rtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
348        mAmPm.setTranslationX((rtl ? 1 : -1) * mTime.getWidth() * (1 - mTime.getScaleX()));
349    }
350
351    private void updateBatteryLevelPaddingEnd() {
352        mBatteryLevel.setPaddingRelative(0, 0,
353                mKeyguardShowing && !mExpanded ? 0 : mBatteryPaddingEnd, 0);
354    }
355
356    @Override
357    public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
358        mBatteryLevel.setText(getResources().getString(R.string.battery_level_template, level));
359        boolean changed = mCharging != charging;
360        mCharging = charging;
361        if (changed) {
362            updateVisibilities();
363        }
364    }
365
366    private void updateClickTargets() {
367        setClickable(!mKeyguardShowing || mExpanded);
368        mDateTime.setClickable(mExpanded);
369        mMultiUserSwitch.setClickable(mExpanded);
370        mSystemIconsSuperContainer.setClickable(mExpanded);
371    }
372
373    private void updateZTranslation() {
374
375        // If we are on the Keyguard, we need to set our z position to zero, so we don't get
376        // shadows.
377        if (mKeyguardShowing && !mExpanded) {
378            setZ(0);
379        } else {
380            setTranslationZ(0);
381        }
382    }
383
384    private void updatePadding() {
385        boolean padded = !mKeyguardShowing || mExpanded;
386        int padding = padded ? mPadding : 0;
387        setPaddingRelative(padding, 0, padding, 0);
388    }
389
390    private void updateClockLp() {
391        int marginBottom = mExpanded && !mOverscrolled ? mClockMarginBottomExpanded : 0;
392        LayoutParams lp = (LayoutParams) mDateTime.getLayoutParams();
393        int rule = mExpanded && !mOverscrolled ? TRUE : 0;
394        if (marginBottom != lp.bottomMargin
395                || lp.getRules()[RelativeLayout.ALIGN_PARENT_BOTTOM] != rule) {
396            lp.bottomMargin = marginBottom;
397            lp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, rule);
398            mDateTime.setLayoutParams(lp);
399        }
400    }
401
402    private void updateMultiUserSwitch() {
403        int marginEnd;
404        if (mExpanded && !mOverscrolled) {
405            marginEnd = mMultiUserExpandedMargin;
406        } else if (mKeyguardShowing) {
407            marginEnd = mMultiUserKeyguardMargin;
408        } else {
409            marginEnd = mMultiUserCollapsedMargin;
410        }
411        int width = mExpanded && !mOverscrolled
412                ? mMultiUserSwitchWidthExpanded
413                : mMultiUserSwitchWidthCollapsed;
414        MarginLayoutParams lp = (MarginLayoutParams) mMultiUserSwitch.getLayoutParams();
415        if (marginEnd != lp.getMarginEnd() || lp.width != width) {
416            lp.setMarginEnd(marginEnd);
417            lp.width = width;
418            mMultiUserSwitch.setLayoutParams(lp);
419        }
420    }
421
422    public void setExpansion(float t) {
423        float height = mCollapsedHeight + t * (mExpandedHeight - mCollapsedHeight);
424        if (height < mCollapsedHeight) {
425            height = mCollapsedHeight;
426        }
427        if (height > mExpandedHeight) {
428            height = mExpandedHeight;
429        }
430        setClipping(height);
431    }
432
433    private void setClipping(float height) {
434        mClipBounds.set(getPaddingLeft(), 0, getWidth() - getPaddingRight(), (int) height);
435        setClipBounds(mClipBounds);
436        invalidateOutline();
437    }
438
439    public void attachSystemIcons(LinearLayout systemIcons) {
440        mSystemIconsContainer.addView(systemIcons);
441        mStatusIcons = systemIcons.findViewById(R.id.statusIcons);
442        mSignalCluster = systemIcons.findViewById(R.id.signal_cluster);
443        mSignalCluster.addOnLayoutChangeListener(mSignalClusterChanged);
444    }
445
446    public void onSystemIconsDetached() {
447        if (mStatusIcons != null) {
448            mStatusIcons.setVisibility(View.VISIBLE);
449        }
450        if (mSignalCluster != null) {
451            mSignalCluster.removeOnLayoutChangeListener(mSignalClusterChanged);
452            mSignalCluster.setVisibility(View.VISIBLE);
453        }
454        mStatusIcons = null;
455        mSignalCluster = null;
456    }
457
458    public void setKeyguardShowing(boolean keyguardShowing) {
459        mKeyguardShowing = keyguardShowing;
460        updateHeights();
461        updateWidth();
462        updateVisibilities();
463        updateZTranslation();
464        updatePadding();
465        updateMultiUserSwitch();
466        updateClickTargets();
467        updateBatteryLevelPaddingEnd();
468    }
469
470    public void setUserInfoController(UserInfoController userInfoController) {
471        userInfoController.addListener(new UserInfoController.OnUserInfoChangedListener() {
472            @Override
473            public void onUserInfoChanged(String name, Drawable picture) {
474                mMultiUserAvatar.setImageDrawable(picture);
475            }
476        });
477    }
478
479    @Override
480    public void onClick(View v) {
481        if (v == mSettingsButton) {
482            startSettingsActivity();
483        } else if (v == mSystemIconsSuperContainer) {
484            startBatteryActivity();
485        }
486    }
487
488    private void startSettingsActivity() {
489        mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS));
490    }
491
492    private void startBatteryActivity() {
493        mActivityStarter.startActivity(new Intent(Intent.ACTION_POWER_USAGE_SUMMARY));
494    }
495
496    public void setQSPanel(QSPanel qsp) {
497        mQSPanel = qsp;
498        if (mQSPanel != null) {
499            mQSPanel.setCallback(mQsPanelCallback);
500        }
501        mMultiUserSwitch.setQsPanel(qsp);
502    }
503
504    @Override
505    public boolean shouldDelayChildPressedState() {
506        return true;
507    }
508
509    public void setShowEmergencyCallsOnly(boolean show) {
510        mShowEmergencyCallsOnly = show;
511        if (mExpanded) {
512            updateVisibilities();
513        }
514    }
515
516    public void setKeyguardUserSwitcherShowing(boolean showing) {
517        // STOPSHIP: NOT CALLED PROPERLY WHEN GOING TO FULL SHADE AND RETURNING!?!
518        mKeyguardUserSwitcherShowing = showing;
519        updateVisibilities();
520        updateSystemIconsLayoutParams();
521    }
522
523    @Override
524    public boolean hasOverlappingRendering() {
525        return !mKeyguardShowing || mExpanded;
526    }
527
528    @Override
529    protected void dispatchSetPressed(boolean pressed) {
530        // We don't want that everything lights up when we click on the header, so block the request
531        // here.
532    }
533
534    private final OnLayoutChangeListener mSignalClusterChanged = new OnLayoutChangeListener() {
535        private final Rect mClipBounds = new Rect();
536
537        @Override
538        public void onLayoutChange(View v, int left, int top, int right,
539                int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
540            // Hide the statusIcons in the header by clipping them.  Can't touch visibility since
541            // they are mirrored to the real status bar.
542            mClipBounds.set(left, 0, mSystemIconsContainer.getWidth(),
543                    mSystemIconsContainer.getHeight());
544            mSystemIconsContainer.setClipBounds(mClipBounds);
545        }
546    };
547
548    private final QSPanel.Callback mQsPanelCallback = new QSPanel.Callback() {
549        @Override
550        public void onToggleStateChanged(final boolean state) {
551            post(new Runnable() {
552                @Override
553                public void run() {
554                    handleToggleStateChanged(state);
555                }
556            });
557        }
558
559        @Override
560        public void onShowingDetail(final QSTile.DetailAdapter detail) {
561            post(new Runnable() {
562                @Override
563                public void run() {
564                    handleShowingDetail(detail);
565                }
566            });
567        }
568
569        @Override
570        public void onScanStateChanged(final boolean state) {
571            post(new Runnable() {
572                @Override
573                public void run() {
574                    handleScanStateChanged(state);
575                }
576            });
577        }
578
579        private void handleToggleStateChanged(boolean state) {
580            mQsDetailHeaderSwitch.setChecked(state);
581        }
582
583        private void handleScanStateChanged(boolean state) {
584            // TODO - waiting on framework asset
585        }
586
587        private void handleShowingDetail(final QSTile.DetailAdapter detail) {
588            final boolean showingDetail = detail != null;
589            transition(mDateTime, !showingDetail);
590            transition(mQsDetailHeader, showingDetail);
591            if (showingDetail) {
592                mQsDetailHeaderTitle.setText(detail.getTitle());
593                final Boolean toggleState = detail.getToggleState();
594                if (toggleState == null) {
595                    mQsDetailHeaderSwitch.setVisibility(INVISIBLE);
596                    mQsDetailHeader.setClickable(false);
597                } else {
598                    mQsDetailHeaderSwitch.setVisibility(VISIBLE);
599                    mQsDetailHeaderSwitch.setChecked(toggleState);
600                    mQsDetailHeader.setClickable(true);
601                    mQsDetailHeader.setOnClickListener(new OnClickListener() {
602                        @Override
603                        public void onClick(View v) {
604                            detail.setToggleState(!mQsDetailHeaderSwitch.isChecked());
605                        }
606                    });
607                }
608            } else {
609                mQsDetailHeader.setClickable(false);
610            }
611        }
612
613        private void transition(final View v, final boolean in) {
614            if (in) {
615                v.bringToFront();
616            }
617            v.animate().alpha(in ? 1 : 0).withLayer().start();
618        }
619    };
620}
621