StatusBarHeaderView.java revision 604ae091429f3ade44daaed25014ee7fbb35bcff
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 boolean getOutline(View view, Outline outline) {
152                outline.setRect(mClipBounds);
153                return true;
154            }
155        });
156    }
157
158    private void loadDimens() {
159        mCollapsedHeight = getResources().getDimensionPixelSize(R.dimen.status_bar_header_height);
160        mExpandedHeight = getResources().getDimensionPixelSize(
161                R.dimen.status_bar_header_height_expanded);
162        mKeyguardHeight = getResources().getDimensionPixelSize(
163                R.dimen.status_bar_header_height_keyguard);
164        mNormalWidth = getLayoutParams().width;
165        mPadding = getResources().getDimensionPixelSize(R.dimen.notification_side_padding);
166        mMultiUserExpandedMargin =
167                getResources().getDimensionPixelSize(R.dimen.multi_user_switch_expanded_margin);
168        mMultiUserCollapsedMargin =
169                getResources().getDimensionPixelSize(R.dimen.multi_user_switch_collapsed_margin);
170        mMultiUserKeyguardMargin =
171                getResources().getDimensionPixelSize(R.dimen.multi_user_switch_keyguard_margin);
172        mSystemIconsSwitcherHiddenExpandedMargin = getResources().getDimensionPixelSize(
173                R.dimen.system_icons_switcher_hidden_expanded_margin);
174        mClockMarginBottomExpanded =
175                getResources().getDimensionPixelSize(R.dimen.clock_expanded_bottom_margin);
176        mMultiUserSwitchWidthCollapsed =
177                getResources().getDimensionPixelSize(R.dimen.multi_user_switch_width_collapsed);
178        mMultiUserSwitchWidthExpanded =
179                getResources().getDimensionPixelSize(R.dimen.multi_user_switch_width_expanded);
180        mAvatarCollapsedScaleFactor =
181                getResources().getDimensionPixelSize(R.dimen.multi_user_avatar_collapsed_size)
182                / (float) mMultiUserAvatar.getLayoutParams().width;
183        mClockCollapsedScaleFactor =
184                (float) getResources().getDimensionPixelSize(R.dimen.qs_time_collapsed_size)
185                / (float) getResources().getDimensionPixelSize(R.dimen.qs_time_expanded_size);
186        mBatteryPaddingEnd =
187                getResources().getDimensionPixelSize(R.dimen.battery_level_padding_end);
188    }
189
190    public void setActivityStarter(ActivityStarter activityStarter) {
191        mActivityStarter = activityStarter;
192    }
193
194    public void setBatteryController(BatteryController batteryController) {
195        mBatteryController = batteryController;
196    }
197
198    public int getCollapsedHeight() {
199        return mKeyguardShowing ? mKeyguardHeight : mCollapsedHeight;
200    }
201
202    public int getExpandedHeight() {
203        return mExpandedHeight;
204    }
205
206    public void setListening(boolean listening) {
207        if (listening == mListening) {
208            return;
209        }
210        mListening = listening;
211        updateBatteryListening();
212    }
213
214    public void setExpanded(boolean expanded, boolean overscrolled) {
215        boolean changed = expanded != mExpanded;
216        boolean overscrollChanged = overscrolled != mOverscrolled;
217        mExpanded = expanded;
218        mOverscrolled = overscrolled;
219        if (changed || overscrollChanged) {
220            updateHeights();
221            updateVisibilities();
222            updateSystemIconsLayoutParams();
223            updateZTranslation();
224            updateClickTargets();
225            updateWidth();
226            updatePadding();
227            updateMultiUserSwitch();
228            if (mQSPanel != null) {
229                mQSPanel.setExpanded(expanded && !overscrolled);
230            }
231            updateClockScale();
232            updateAvatarScale();
233            updateClockLp();
234            updateBatteryLevelPaddingEnd();
235        }
236    }
237
238    private void updateHeights() {
239        boolean onKeyguardAndCollapsed = mKeyguardShowing && !mExpanded;
240        int height;
241        if (mExpanded) {
242            height = mExpandedHeight;
243        } else if (onKeyguardAndCollapsed) {
244            height = mKeyguardHeight;
245        } else {
246            height = mCollapsedHeight;
247        }
248        ViewGroup.LayoutParams lp = getLayoutParams();
249        if (lp.height != height) {
250            lp.height = height;
251            setLayoutParams(lp);
252        }
253        int systemIconsContainerHeight = onKeyguardAndCollapsed ? mKeyguardHeight : mCollapsedHeight;
254        lp = mSystemIconsSuperContainer.getLayoutParams();
255        if (lp.height != systemIconsContainerHeight) {
256            lp.height = systemIconsContainerHeight;
257            mSystemIconsSuperContainer.setLayoutParams(lp);
258        }
259        lp = mMultiUserSwitch.getLayoutParams();
260        if (lp.height != systemIconsContainerHeight) {
261            lp.height = systemIconsContainerHeight;
262            mMultiUserSwitch.setLayoutParams(lp);
263        }
264    }
265
266    private void updateWidth() {
267        int width = (mKeyguardShowing && !mExpanded) ? mKeyguardWidth : mNormalWidth;
268        ViewGroup.LayoutParams lp = getLayoutParams();
269        if (width != lp.width) {
270            lp.width = width;
271            setLayoutParams(lp);
272        }
273    }
274
275    private void updateVisibilities() {
276        boolean onKeyguardAndCollapsed = mKeyguardShowing && !mExpanded;
277        if (onKeyguardAndCollapsed) {
278            setBackground(null);
279        } else {
280            setBackgroundResource(R.drawable.notification_header_bg);
281        }
282        mDateTime.setVisibility(onKeyguardAndCollapsed ? View.INVISIBLE : View.VISIBLE);
283        mKeyguardCarrierText.setVisibility(onKeyguardAndCollapsed ? View.VISIBLE : View.GONE);
284        mDateCollapsed.setVisibility(mExpanded && !mOverscrolled ? View.GONE : View.VISIBLE);
285        mDateExpanded.setVisibility(mExpanded && !mOverscrolled ? View.VISIBLE : View.GONE);
286        mSettingsButton.setVisibility(mExpanded && !mOverscrolled ? View.VISIBLE : View.GONE);
287        mQsDetailHeader.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
288        if (mStatusIcons != null) {
289            mStatusIcons.setVisibility(!mExpanded || mOverscrolled ? View.VISIBLE : View.GONE);
290        }
291        if (mSignalCluster != null) {
292            mSignalCluster.setVisibility(!mExpanded || mOverscrolled ? View.VISIBLE : View.GONE);
293        }
294        mEmergencyCallsOnly.setVisibility(mExpanded && !mOverscrolled && mShowEmergencyCallsOnly
295                ? VISIBLE : GONE);
296        mMultiUserSwitch.setVisibility(mExpanded || !mKeyguardUserSwitcherShowing
297                ? VISIBLE : GONE);
298        mBatteryLevel.setVisibility(mKeyguardShowing && mCharging || mExpanded && !mOverscrolled
299                ? View.VISIBLE : View.GONE);
300    }
301
302    private void updateSystemIconsLayoutParams() {
303        RelativeLayout.LayoutParams lp = (LayoutParams) mSystemIconsSuperContainer.getLayoutParams();
304        lp.addRule(RelativeLayout.START_OF, mExpanded && !mOverscrolled
305                ? mSettingsButton.getId()
306                : mMultiUserSwitch.getId());
307        lp.removeRule(ALIGN_PARENT_START);
308        if (mMultiUserSwitch.getVisibility() == GONE) {
309            lp.setMarginEnd(mSystemIconsSwitcherHiddenExpandedMargin);
310        } else {
311            lp.setMarginEnd(0);
312        }
313        mSystemIconsSuperContainer.setLayoutParams(lp);
314    }
315
316    private void updateBatteryListening() {
317        if (mListening) {
318            mBatteryController.addStateChangedCallback(this);
319        } else {
320            mBatteryController.removeStateChangedCallback(this);
321        }
322    }
323
324    private void updateAvatarScale() {
325        if (!mExpanded || mOverscrolled) {
326            mMultiUserSwitch.setScaleX(mAvatarCollapsedScaleFactor);
327            mMultiUserSwitch.setScaleY(mAvatarCollapsedScaleFactor);
328        } else {
329            mMultiUserSwitch.setScaleX(1f);
330            mMultiUserSwitch.setScaleY(1f);
331        }
332    }
333
334    private void updateClockScale() {
335        mAmPm.setScaleX(mClockCollapsedScaleFactor);
336        mAmPm.setScaleY(mClockCollapsedScaleFactor);
337        if (!mExpanded || mOverscrolled) {
338            mTime.setScaleX(mClockCollapsedScaleFactor);
339            mTime.setScaleY(mClockCollapsedScaleFactor);
340        } else {
341            mTime.setScaleX(1f);
342            mTime.setScaleY(1f);
343        }
344        updateAmPmTranslation();
345    }
346
347    private void updateAmPmTranslation() {
348        boolean rtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
349        mAmPm.setTranslationX((rtl ? 1 : -1) * mTime.getWidth() * (1 - mTime.getScaleX()));
350    }
351
352    private void updateBatteryLevelPaddingEnd() {
353        mBatteryLevel.setPaddingRelative(0, 0,
354                mKeyguardShowing && !mExpanded ? 0 : mBatteryPaddingEnd, 0);
355    }
356
357    @Override
358    public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
359        mBatteryLevel.setText(getResources().getString(R.string.battery_level_template, level));
360        boolean changed = mCharging != charging;
361        mCharging = charging;
362        if (changed) {
363            updateVisibilities();
364        }
365    }
366
367    private void updateClickTargets() {
368        setClickable(!mKeyguardShowing || mExpanded);
369        mDateTime.setClickable(mExpanded);
370        mMultiUserSwitch.setClickable(mExpanded);
371        mSystemIconsSuperContainer.setClickable(mExpanded);
372    }
373
374    private void updateZTranslation() {
375
376        // If we are on the Keyguard, we need to set our z position to zero, so we don't get
377        // shadows.
378        if (mKeyguardShowing && !mExpanded) {
379            setZ(0);
380        } else {
381            setTranslationZ(0);
382        }
383    }
384
385    private void updatePadding() {
386        boolean padded = !mKeyguardShowing || mExpanded;
387        int padding = padded ? mPadding : 0;
388        setPaddingRelative(padding, 0, padding, 0);
389    }
390
391    private void updateClockLp() {
392        int marginBottom = mExpanded && !mOverscrolled ? mClockMarginBottomExpanded : 0;
393        LayoutParams lp = (LayoutParams) mDateTime.getLayoutParams();
394        int rule = mExpanded && !mOverscrolled ? TRUE : 0;
395        if (marginBottom != lp.bottomMargin
396                || lp.getRules()[RelativeLayout.ALIGN_PARENT_BOTTOM] != rule) {
397            lp.bottomMargin = marginBottom;
398            lp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, rule);
399            mDateTime.setLayoutParams(lp);
400        }
401    }
402
403    private void updateMultiUserSwitch() {
404        int marginEnd;
405        if (mExpanded && !mOverscrolled) {
406            marginEnd = mMultiUserExpandedMargin;
407        } else if (mKeyguardShowing) {
408            marginEnd = mMultiUserKeyguardMargin;
409        } else {
410            marginEnd = mMultiUserCollapsedMargin;
411        }
412        int width = mExpanded && !mOverscrolled
413                ? mMultiUserSwitchWidthExpanded
414                : mMultiUserSwitchWidthCollapsed;
415        MarginLayoutParams lp = (MarginLayoutParams) mMultiUserSwitch.getLayoutParams();
416        if (marginEnd != lp.getMarginEnd() || lp.width != width) {
417            lp.setMarginEnd(marginEnd);
418            lp.width = width;
419            mMultiUserSwitch.setLayoutParams(lp);
420        }
421    }
422
423    public void setExpansion(float t) {
424        float height = mCollapsedHeight + t * (mExpandedHeight - mCollapsedHeight);
425        if (height < mCollapsedHeight) {
426            height = mCollapsedHeight;
427        }
428        if (height > mExpandedHeight) {
429            height = mExpandedHeight;
430        }
431        setClipping(height);
432    }
433
434    private void setClipping(float height) {
435        mClipBounds.set(getPaddingLeft(), 0, getWidth() - getPaddingRight(), (int) height);
436        setClipBounds(mClipBounds);
437        invalidateOutline();
438    }
439
440    public void attachSystemIcons(LinearLayout systemIcons) {
441        mSystemIconsContainer.addView(systemIcons);
442        mStatusIcons = systemIcons.findViewById(R.id.statusIcons);
443        mSignalCluster = systemIcons.findViewById(R.id.signal_cluster);
444        mSignalCluster.addOnLayoutChangeListener(mSignalClusterChanged);
445    }
446
447    public void onSystemIconsDetached() {
448        if (mStatusIcons != null) {
449            mStatusIcons.setVisibility(View.VISIBLE);
450        }
451        if (mSignalCluster != null) {
452            mSignalCluster.removeOnLayoutChangeListener(mSignalClusterChanged);
453            mSignalCluster.setVisibility(View.VISIBLE);
454        }
455        mStatusIcons = null;
456        mSignalCluster = null;
457    }
458
459    public void setKeyguardShowing(boolean keyguardShowing) {
460        mKeyguardShowing = keyguardShowing;
461        updateHeights();
462        updateWidth();
463        updateVisibilities();
464        updateZTranslation();
465        updatePadding();
466        updateMultiUserSwitch();
467        updateClickTargets();
468        updateBatteryLevelPaddingEnd();
469    }
470
471    public void setUserInfoController(UserInfoController userInfoController) {
472        userInfoController.addListener(new UserInfoController.OnUserInfoChangedListener() {
473            @Override
474            public void onUserInfoChanged(String name, Drawable picture) {
475                mMultiUserAvatar.setImageDrawable(picture);
476            }
477        });
478    }
479
480    @Override
481    public void onClick(View v) {
482        if (v == mSettingsButton) {
483            startSettingsActivity();
484        } else if (v == mSystemIconsSuperContainer) {
485            startBatteryActivity();
486        }
487    }
488
489    private void startSettingsActivity() {
490        mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS));
491    }
492
493    private void startBatteryActivity() {
494        mActivityStarter.startActivity(new Intent(Intent.ACTION_POWER_USAGE_SUMMARY));
495    }
496
497    public void setQSPanel(QSPanel qsp) {
498        mQSPanel = qsp;
499        if (mQSPanel != null) {
500            mQSPanel.setCallback(mQsPanelCallback);
501        }
502        mMultiUserSwitch.setQsPanel(qsp);
503    }
504
505    @Override
506    public boolean shouldDelayChildPressedState() {
507        return true;
508    }
509
510    public void setShowEmergencyCallsOnly(boolean show) {
511        mShowEmergencyCallsOnly = show;
512        if (mExpanded) {
513            updateVisibilities();
514        }
515    }
516
517    public void setKeyguardUserSwitcherShowing(boolean showing) {
518        // STOPSHIP: NOT CALLED PROPERLY WHEN GOING TO FULL SHADE AND RETURNING!?!
519        mKeyguardUserSwitcherShowing = showing;
520        updateVisibilities();
521        updateSystemIconsLayoutParams();
522    }
523
524    @Override
525    public boolean hasOverlappingRendering() {
526        return !mKeyguardShowing || mExpanded;
527    }
528
529    @Override
530    protected void dispatchSetPressed(boolean pressed) {
531        // We don't want that everything lights up when we click on the header, so block the request
532        // here.
533    }
534
535    private final OnLayoutChangeListener mSignalClusterChanged = new OnLayoutChangeListener() {
536        private final Rect mClipBounds = new Rect();
537
538        @Override
539        public void onLayoutChange(View v, int left, int top, int right,
540                int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
541            // Hide the statusIcons in the header by clipping them.  Can't touch visibility since
542            // they are mirrored to the real status bar.
543            mClipBounds.set(left, 0, mSystemIconsContainer.getWidth(),
544                    mSystemIconsContainer.getHeight());
545            mSystemIconsContainer.setClipBounds(mClipBounds);
546        }
547    };
548
549    private final QSPanel.Callback mQsPanelCallback = new QSPanel.Callback() {
550        @Override
551        public void onToggleStateChanged(final boolean state) {
552            post(new Runnable() {
553                @Override
554                public void run() {
555                    handleToggleStateChanged(state);
556                }
557            });
558        }
559
560        @Override
561        public void onShowingDetail(final QSTile.DetailAdapter detail) {
562            post(new Runnable() {
563                @Override
564                public void run() {
565                    handleShowingDetail(detail);
566                }
567            });
568        }
569
570        @Override
571        public void onScanStateChanged(final boolean state) {
572            post(new Runnable() {
573                @Override
574                public void run() {
575                    handleScanStateChanged(state);
576                }
577            });
578        }
579
580        private void handleToggleStateChanged(boolean state) {
581            mQsDetailHeaderSwitch.setChecked(state);
582        }
583
584        private void handleScanStateChanged(boolean state) {
585            // TODO - waiting on framework asset
586        }
587
588        private void handleShowingDetail(final QSTile.DetailAdapter detail) {
589            final boolean showingDetail = detail != null;
590            transition(mDateTime, !showingDetail);
591            transition(mQsDetailHeader, showingDetail);
592            if (showingDetail) {
593                mQsDetailHeaderTitle.setText(detail.getTitle());
594                final Boolean toggleState = detail.getToggleState();
595                if (toggleState == null) {
596                    mQsDetailHeaderSwitch.setVisibility(INVISIBLE);
597                    mQsDetailHeader.setClickable(false);
598                } else {
599                    mQsDetailHeaderSwitch.setVisibility(VISIBLE);
600                    mQsDetailHeaderSwitch.setChecked(toggleState);
601                    mQsDetailHeader.setClickable(true);
602                    mQsDetailHeader.setOnClickListener(new OnClickListener() {
603                        @Override
604                        public void onClick(View v) {
605                            detail.setToggleState(!mQsDetailHeaderSwitch.isChecked());
606                        }
607                    });
608                }
609            } else {
610                mQsDetailHeader.setClickable(false);
611            }
612        }
613
614        private void transition(final View v, final boolean in) {
615            if (in) {
616                v.bringToFront();
617            }
618            v.animate().alpha(in ? 1 : 0).withLayer().start();
619        }
620    };
621}
622