OSDN Git Service

Remove unnecessary casts on calls to findViewById
[android-x86/frameworks-base.git] / packages / SystemUI / src / com / android / systemui / statusbar / NotificationShelf.java
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16
17 package com.android.systemui.statusbar;
18
19 import android.content.Context;
20 import android.content.res.Configuration;
21 import android.os.SystemProperties;
22 import android.util.AttributeSet;
23 import android.view.View;
24 import android.view.ViewGroup;
25 import android.view.accessibility.AccessibilityNodeInfo;
26
27 import com.android.systemui.Interpolators;
28 import com.android.systemui.R;
29 import com.android.systemui.ViewInvertHelper;
30 import com.android.systemui.statusbar.notification.NotificationUtils;
31 import com.android.systemui.statusbar.phone.NotificationIconContainer;
32 import com.android.systemui.statusbar.phone.NotificationPanelView;
33 import com.android.systemui.statusbar.stack.AmbientState;
34 import com.android.systemui.statusbar.stack.AnimationProperties;
35 import com.android.systemui.statusbar.stack.ExpandableViewState;
36 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
37 import com.android.systemui.statusbar.stack.StackScrollState;
38 import com.android.systemui.statusbar.stack.ViewState;
39
40 /**
41  * A notification shelf view that is placed inside the notification scroller. It manages the
42  * overflow icons that don't fit into the regular list anymore.
43  */
44 public class NotificationShelf extends ActivatableNotificationView implements
45         View.OnLayoutChangeListener {
46
47     public static final boolean SHOW_AMBIENT_ICONS = true;
48     private static final boolean USE_ANIMATIONS_WHEN_OPENING =
49             SystemProperties.getBoolean("debug.icon_opening_animations", true);
50     private static final boolean ICON_ANMATIONS_WHILE_SCROLLING
51             = SystemProperties.getBoolean("debug.icon_scroll_animations", true);
52     private ViewInvertHelper mViewInvertHelper;
53     private boolean mDark;
54     private NotificationIconContainer mShelfIcons;
55     private ShelfState mShelfState;
56     private int[] mTmp = new int[2];
57     private boolean mHideBackground;
58     private int mIconAppearTopPadding;
59     private int mStatusBarHeight;
60     private int mStatusBarPaddingStart;
61     private AmbientState mAmbientState;
62     private NotificationStackScrollLayout mHostLayout;
63     private int mMaxLayoutHeight;
64     private int mPaddingBetweenElements;
65     private int mNotGoneIndex;
66     private boolean mHasItemsInStableShelf;
67     private NotificationIconContainer mCollapsedIcons;
68     private int mScrollFastThreshold;
69     private int mStatusBarState;
70     private float mMaxShelfEnd;
71     private int mRelativeOffset;
72     private boolean mInteractive;
73     private boolean mAnimationsEnabled = true;
74
75     public NotificationShelf(Context context, AttributeSet attrs) {
76         super(context, attrs);
77     }
78
79     @Override
80     protected void onFinishInflate() {
81         super.onFinishInflate();
82         mShelfIcons = findViewById(R.id.content);
83         mShelfIcons.setClipChildren(false);
84         mShelfIcons.setClipToPadding(false);
85
86         setClipToActualHeight(false);
87         setClipChildren(false);
88         setClipToPadding(false);
89         mShelfIcons.setShowAllIcons(false);
90         mViewInvertHelper = new ViewInvertHelper(mShelfIcons,
91                 NotificationPanelView.DOZE_ANIMATION_DURATION);
92         mShelfState = new ShelfState();
93         initDimens();
94     }
95
96     public void bind(AmbientState ambientState, NotificationStackScrollLayout hostLayout) {
97         mAmbientState = ambientState;
98         mHostLayout = hostLayout;
99     }
100
101     private void initDimens() {
102         mIconAppearTopPadding = getResources().getDimensionPixelSize(
103                 R.dimen.notification_icon_appear_padding);
104         mStatusBarHeight = getResources().getDimensionPixelOffset(R.dimen.status_bar_height);
105         mStatusBarPaddingStart = getResources().getDimensionPixelOffset(
106                 R.dimen.status_bar_padding_start);
107         mPaddingBetweenElements = getResources().getDimensionPixelSize(
108                 R.dimen.notification_divider_height);
109         ViewGroup.LayoutParams layoutParams = getLayoutParams();
110         layoutParams.height = getResources().getDimensionPixelOffset(
111                 R.dimen.notification_shelf_height);
112         setLayoutParams(layoutParams);
113         int padding = getResources().getDimensionPixelOffset(R.dimen.shelf_icon_container_padding);
114         mShelfIcons.setPadding(padding, 0, padding, 0);
115         mScrollFastThreshold = getResources().getDimensionPixelOffset(
116                 R.dimen.scroll_fast_threshold);
117     }
118
119     @Override
120     protected void onConfigurationChanged(Configuration newConfig) {
121         super.onConfigurationChanged(newConfig);
122         initDimens();
123     }
124
125     @Override
126     public void setDark(boolean dark, boolean fade, long delay) {
127         super.setDark(dark, fade, delay);
128         if (mDark == dark) return;
129         mDark = dark;
130         mShelfIcons.setDark(dark, fade, delay);
131         updateInteractiveness();
132     }
133
134     @Override
135     protected View getContentView() {
136         return mShelfIcons;
137     }
138
139     public NotificationIconContainer getShelfIcons() {
140         return mShelfIcons;
141     }
142
143     @Override
144     public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
145         return mShelfState;
146     }
147
148     public void updateState(StackScrollState resultState,
149             AmbientState ambientState) {
150         View lastView = ambientState.getLastVisibleBackgroundChild();
151         if (lastView != null) {
152             float maxShelfEnd = ambientState.getInnerHeight() + ambientState.getTopPadding()
153                     + ambientState.getStackTranslation();
154             ExpandableViewState lastViewState = resultState.getViewStateForView(lastView);
155             float viewEnd = lastViewState.yTranslation + lastViewState.height;
156             mShelfState.copyFrom(lastViewState);
157             mShelfState.height = getIntrinsicHeight();
158             mShelfState.yTranslation = Math.max(Math.min(viewEnd, maxShelfEnd) - mShelfState.height,
159                     getFullyClosedTranslation());
160             mShelfState.zTranslation = ambientState.getBaseZHeight();
161             float openedAmount = (mShelfState.yTranslation - getFullyClosedTranslation())
162                     / (getIntrinsicHeight() * 2);
163             openedAmount = Math.min(1.0f, openedAmount);
164             mShelfState.openedAmount = openedAmount;
165             mShelfState.clipTopAmount = 0;
166             mShelfState.alpha = mAmbientState.isPulsing() ? 0 : 1;
167             mShelfState.belowSpeedBump = mAmbientState.getSpeedBumpIndex() == 0;
168             mShelfState.shadowAlpha = 1.0f;
169             mShelfState.hideSensitive = false;
170             mShelfState.xTranslation = getTranslationX();
171             if (mNotGoneIndex != -1) {
172                 mShelfState.notGoneIndex = Math.min(mShelfState.notGoneIndex, mNotGoneIndex);
173             }
174             mShelfState.hasItemsInStableShelf = lastViewState.inShelf;
175             mShelfState.hidden = !mAmbientState.isShadeExpanded();
176             mShelfState.maxShelfEnd = maxShelfEnd;
177         } else {
178             mShelfState.hidden = true;
179             mShelfState.location = ExpandableViewState.LOCATION_GONE;
180             mShelfState.hasItemsInStableShelf = false;
181         }
182     }
183
184     /**
185      * Update the shelf appearance based on the other notifications around it. This transforms
186      * the icons from the notification area into the shelf.
187      */
188     public void updateAppearance() {
189         mShelfIcons.resetViewStates();
190         float shelfStart = getTranslationY();
191         float numViewsInShelf = 0.0f;
192         View lastChild = mAmbientState.getLastVisibleBackgroundChild();
193         mNotGoneIndex = -1;
194         float interpolationStart = mMaxLayoutHeight - getIntrinsicHeight() * 2;
195         float expandAmount = 0.0f;
196         if (shelfStart >= interpolationStart) {
197             expandAmount = (shelfStart - interpolationStart) / getIntrinsicHeight();
198             expandAmount = Math.min(1.0f, expandAmount);
199         }
200         //  find the first view that doesn't overlap with the shelf
201         int notificationIndex = 0;
202         int notGoneIndex = 0;
203         int colorOfViewBeforeLast = 0;
204         boolean backgroundForceHidden = false;
205         if (mHideBackground && !mShelfState.hasItemsInStableShelf) {
206             backgroundForceHidden = true;
207         }
208         int colorTwoBefore = NO_COLOR;
209         int previousColor = NO_COLOR;
210         float transitionAmount = 0.0f;
211         float currentScrollVelocity = mAmbientState.getCurrentScrollVelocity();
212         boolean scrollingFast = currentScrollVelocity > mScrollFastThreshold
213                 || (mAmbientState.isExpansionChanging()
214                         && Math.abs(mAmbientState.getExpandingVelocity()) > mScrollFastThreshold);
215         boolean scrolling = currentScrollVelocity > 0;
216         boolean expandingAnimated = mAmbientState.isExpansionChanging()
217                 && !mAmbientState.isPanelTracking();
218         int baseZHeight = mAmbientState.getBaseZHeight();
219         while (notificationIndex < mHostLayout.getChildCount()) {
220             ExpandableView child = (ExpandableView) mHostLayout.getChildAt(notificationIndex);
221             notificationIndex++;
222             if (!(child instanceof ExpandableNotificationRow)
223                     || child.getVisibility() == GONE) {
224                 continue;
225             }
226             ExpandableNotificationRow row = (ExpandableNotificationRow) child;
227             float notificationClipEnd;
228             boolean aboveShelf = row.getTranslationZ() > baseZHeight;
229             boolean isLastChild = child == lastChild;
230             float rowTranslationY = row.getTranslationY();
231             if (isLastChild || aboveShelf || backgroundForceHidden) {
232                 notificationClipEnd = shelfStart + getIntrinsicHeight();
233             } else {
234                 notificationClipEnd = shelfStart - mPaddingBetweenElements;
235                 float height = notificationClipEnd - rowTranslationY;
236                 if (!row.isBelowSpeedBump() && height <= getNotificationMergeSize()) {
237                     // We want the gap to close when we reached the minimum size and only shrink
238                     // before
239                     notificationClipEnd = Math.min(shelfStart,
240                             rowTranslationY + getNotificationMergeSize());
241                 }
242             }
243             updateNotificationClipHeight(row, notificationClipEnd);
244             float inShelfAmount = updateIconAppearance(row, expandAmount, scrolling, scrollingFast,
245                     expandingAnimated, isLastChild);
246             numViewsInShelf += inShelfAmount;
247             int ownColorUntinted = row.getBackgroundColorWithoutTint();
248             if (rowTranslationY >= shelfStart && mNotGoneIndex == -1) {
249                 mNotGoneIndex = notGoneIndex;
250                 setTintColor(previousColor);
251                 setOverrideTintColor(colorTwoBefore, transitionAmount);
252
253             } else if (mNotGoneIndex == -1) {
254                 colorTwoBefore = previousColor;
255                 transitionAmount = inShelfAmount;
256             }
257             if (isLastChild && colorOfViewBeforeLast != NO_COLOR) {
258                 row.setOverrideTintColor(colorOfViewBeforeLast, inShelfAmount);
259             } else {
260                 colorOfViewBeforeLast = ownColorUntinted;
261                 row.setOverrideTintColor(NO_COLOR, 0 /* overrideAmount */);
262             }
263             if (notGoneIndex != 0 || !aboveShelf) {
264                 row.setAboveShelf(false);
265             }
266             notGoneIndex++;
267             previousColor = ownColorUntinted;
268         }
269         mShelfIcons.setSpeedBumpIndex(mAmbientState.getSpeedBumpIndex());
270         mShelfIcons.calculateIconTranslations();
271         mShelfIcons.applyIconStates();
272         boolean hideBackground = numViewsInShelf < 1.0f;
273         setHideBackground(hideBackground || backgroundForceHidden);
274         if (mNotGoneIndex == -1) {
275             mNotGoneIndex = notGoneIndex;
276         }
277     }
278
279     private void updateNotificationClipHeight(ExpandableNotificationRow row,
280             float notificationClipEnd) {
281         float viewEnd = row.getTranslationY() + row.getActualHeight();
282         if (viewEnd > notificationClipEnd
283                 && (mAmbientState.isShadeExpanded()
284                         || (!row.isPinned() && !row.isHeadsUpAnimatingAway()))) {
285             row.setClipBottomAmount((int) (viewEnd - notificationClipEnd));
286         } else {
287             row.setClipBottomAmount(0);
288         }
289     }
290
291     /**
292      * @return the icon amount how much this notification is in the shelf;
293      */
294     private float updateIconAppearance(ExpandableNotificationRow row, float expandAmount,
295             boolean scrolling, boolean scrollingFast, boolean expandingAnimated,
296             boolean isLastChild) {
297         // Let calculate how much the view is in the shelf
298         float viewStart = row.getTranslationY();
299         int fullHeight = row.getActualHeight() + mPaddingBetweenElements;
300         float iconTransformDistance = getIntrinsicHeight() * 1.5f;
301         iconTransformDistance *= NotificationUtils.interpolate(1.f, 1.5f, expandAmount);
302         if (isLastChild) {
303             fullHeight = Math.min(fullHeight, row.getMinHeight() - getIntrinsicHeight());
304             iconTransformDistance = Math.min(iconTransformDistance, row.getMinHeight()
305                     - getIntrinsicHeight());
306         }
307         float viewEnd = viewStart + fullHeight;
308         float fullTransitionAmount;
309         float iconTransitionAmount;
310         float shelfStart = getTranslationY();
311         if (viewEnd >= shelfStart && (mAmbientState.isShadeExpanded()
312                 || (!row.isPinned() && !row.isHeadsUpAnimatingAway()))) {
313             if (viewStart < shelfStart) {
314
315                 float fullAmount = (shelfStart - viewStart) / fullHeight;
316                 float interpolatedAmount =  Interpolators.ACCELERATE_DECELERATE.getInterpolation(
317                         fullAmount);
318                 interpolatedAmount = NotificationUtils.interpolate(
319                         interpolatedAmount, fullAmount, expandAmount);
320                 fullTransitionAmount = 1.0f - interpolatedAmount;
321
322                 iconTransitionAmount = (shelfStart - viewStart) / iconTransformDistance;
323                 iconTransitionAmount = Math.min(1.0f, iconTransitionAmount);
324                 iconTransitionAmount = 1.0f - iconTransitionAmount;
325
326             } else {
327                 fullTransitionAmount = 1.0f;
328                 iconTransitionAmount = 1.0f;
329             }
330         } else {
331             fullTransitionAmount = 0.0f;
332             iconTransitionAmount = 0.0f;
333         }
334         updateIconPositioning(row, iconTransitionAmount, fullTransitionAmount,
335                 iconTransformDistance, scrolling, scrollingFast, expandingAnimated, isLastChild);
336         return fullTransitionAmount;
337     }
338
339     private void updateIconPositioning(ExpandableNotificationRow row, float iconTransitionAmount,
340             float fullTransitionAmount, float iconTransformDistance, boolean scrolling,
341             boolean scrollingFast, boolean expandingAnimated, boolean isLastChild) {
342         StatusBarIconView icon = row.getEntry().expandedIcon;
343         NotificationIconContainer.IconState iconState = getIconState(icon);
344         if (iconState == null) {
345             return;
346         }
347         float clampedAmount = iconTransitionAmount > 0.5f ? 1.0f : 0.0f;
348         if (clampedAmount == fullTransitionAmount) {
349             iconState.useFullTransitionAmount = scrollingFast || expandingAnimated
350                 || (!ICON_ANMATIONS_WHILE_SCROLLING && fullTransitionAmount == 0.0f && scrolling);
351             iconState.useLinearTransitionAmount = !ICON_ANMATIONS_WHILE_SCROLLING
352                     && fullTransitionAmount == 0.0f && !mAmbientState.isExpansionChanging();
353             iconState.translateContent = mMaxLayoutHeight - getTranslationY()
354                     - getIntrinsicHeight() > 0;
355         }
356         if (scrollingFast || (expandingAnimated && iconState.useFullTransitionAmount
357                 && !ViewState.isAnimatingY(icon))) {
358             iconState.cancelAnimations(icon);
359             iconState.useFullTransitionAmount = true;
360         }
361         float transitionAmount;
362         if (isLastChild || !USE_ANIMATIONS_WHEN_OPENING || iconState.useFullTransitionAmount
363                 || iconState.useLinearTransitionAmount) {
364             transitionAmount = iconTransitionAmount;
365         } else {
366             // We take the clamped position instead
367             transitionAmount = clampedAmount;
368             iconState.needsCannedAnimation = iconState.clampedAppearAmount != clampedAmount;
369         }
370         iconState.iconAppearAmount = !USE_ANIMATIONS_WHEN_OPENING
371                     || iconState.useFullTransitionAmount
372                 ? fullTransitionAmount
373                 : transitionAmount;
374         iconState.clampedAppearAmount = clampedAmount;
375         float contentTransformationAmount = isLastChild || iconState.translateContent
376                 ? iconTransitionAmount
377                 : 0.0f;
378         row.setContentTransformationAmount(contentTransformationAmount, isLastChild);
379         setIconTransformationAmount(row, transitionAmount, iconTransformDistance,
380                 clampedAmount != transitionAmount, isLastChild);
381     }
382
383     private void setIconTransformationAmount(ExpandableNotificationRow row,
384             float transitionAmount, float iconTransformDistance, boolean usingLinearInterpolation,
385             boolean isLastChild) {
386         StatusBarIconView icon = row.getEntry().expandedIcon;
387         NotificationIconContainer.IconState iconState = getIconState(icon);
388
389         View rowIcon = row.getNotificationIcon();
390         float notificationIconPosition = row.getTranslationY() + row.getContentTranslation();
391         boolean stayingInShelf = row.isInShelf() && !row.isTransformingIntoShelf();
392         if (usingLinearInterpolation && !stayingInShelf) {
393             // If we interpolate from the notification position, this might lead to a slightly
394             // odd interpolation, since the notification position changes as well. Let's interpolate
395             // from a fixed distance. We can only do this if we don't animate and the icon is
396             // always in the interpolated positon.
397             notificationIconPosition = getTranslationY() - iconTransformDistance;
398         }
399         float notificationIconSize = 0.0f;
400         int iconTopPadding;
401         if (rowIcon != null) {
402             iconTopPadding = row.getRelativeTopPadding(rowIcon);
403             notificationIconSize = rowIcon.getHeight();
404         } else {
405             iconTopPadding = mIconAppearTopPadding;
406         }
407         notificationIconPosition += iconTopPadding;
408         float shelfIconPosition = getTranslationY() + icon.getTop();
409         shelfIconPosition += ((1.0f - icon.getIconScale()) * icon.getHeight()) / 2.0f;
410         float iconYTranslation = NotificationUtils.interpolate(
411                 notificationIconPosition - shelfIconPosition,
412                 0,
413                 transitionAmount);
414         float shelfIconSize = icon.getHeight() * icon.getIconScale();
415         float alpha = 1.0f;
416         boolean noIcon = !row.isShowingIcon();
417         if (noIcon) {
418             // The view currently doesn't have an icon, lets transform it in!
419             alpha = transitionAmount;
420             notificationIconSize = shelfIconSize / 2.0f;
421         }
422         // The notification size is different from the size in the shelf / statusbar
423         float newSize = NotificationUtils.interpolate(notificationIconSize, shelfIconSize,
424                 transitionAmount);
425         if (iconState != null) {
426             iconState.scaleX = newSize / icon.getHeight() / icon.getIconScale();
427             iconState.scaleY = iconState.scaleX;
428             iconState.hidden = transitionAmount == 0.0f;
429             iconState.alpha = alpha;
430             iconState.yTranslation = iconYTranslation;
431             if (stayingInShelf) {
432                 iconState.iconAppearAmount = 1.0f;
433                 iconState.alpha = 1.0f;
434                 iconState.scaleX = 1.0f;
435                 iconState.scaleY = 1.0f;
436                 iconState.hidden = false;
437             }
438             if (row.isAboveShelf() || (!row.isInShelf() && (isLastChild && row.areGutsExposed()
439                     || row.getTranslationZ() > mAmbientState.getBaseZHeight()))) {
440                 iconState.hidden = true;
441             }
442             int shelfColor = icon.getStaticDrawableColor();
443             if (!noIcon && shelfColor != StatusBarIconView.NO_COLOR) {
444                 int notificationColor
445                         = row.getVisibleNotificationHeader().getOriginalNotificationColor();
446                 shelfColor = NotificationUtils.interpolateColors(notificationColor, shelfColor,
447                         iconState.iconAppearAmount);
448             }
449             iconState.iconColor = shelfColor;
450         }
451     }
452
453     private NotificationIconContainer.IconState getIconState(StatusBarIconView icon) {
454         return mShelfIcons.getIconState(icon);
455     }
456
457     private float getFullyClosedTranslation() {
458         return - (getIntrinsicHeight() - mStatusBarHeight) / 2;
459     }
460
461     public int getNotificationMergeSize() {
462         return getIntrinsicHeight();
463     }
464
465     @Override
466     public boolean hasNoContentHeight() {
467         return true;
468     }
469
470     private void setHideBackground(boolean hideBackground) {
471         if (mHideBackground != hideBackground) {
472             mHideBackground = hideBackground;
473             updateBackground();
474             updateOutline();
475         }
476     }
477
478     public boolean hidesBackground() {
479         return mHideBackground;
480     }
481
482     @Override
483     protected boolean needsOutline() {
484         return !mHideBackground && super.needsOutline();
485     }
486
487     @Override
488     protected boolean shouldHideBackground() {
489         return super.shouldHideBackground() || mHideBackground;
490     }
491
492     @Override
493     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
494         super.onLayout(changed, left, top, right, bottom);
495         updateRelativeOffset();
496     }
497
498     private void updateRelativeOffset() {
499         mCollapsedIcons.getLocationOnScreen(mTmp);
500         mRelativeOffset = mTmp[0];
501         getLocationOnScreen(mTmp);
502         mRelativeOffset -= mTmp[0];
503     }
504
505     private void setOpenedAmount(float openedAmount) {
506         if (!mAmbientState.isPanelFullWidth()) {
507             // We don't do a transformation at all, lets just assume we are fully opened
508             openedAmount = 1.0f;
509         }
510         int start = mRelativeOffset;
511         if (isLayoutRtl()) {
512             start = getWidth() - start - mCollapsedIcons.getWidth();
513         }
514         int width = (int) NotificationUtils.interpolate(start + mCollapsedIcons.getWidth(),
515                 mShelfIcons.getWidth(),
516                 openedAmount);
517         mShelfIcons.setActualLayoutWidth(width);
518         boolean hasOverflow = mCollapsedIcons.hasOverflow();
519         int collapsedPadding = mCollapsedIcons.getPaddingEnd();
520         if (!hasOverflow) {
521             // we have to ensure that adding the low priority notification won't lead to an
522             // overflow
523             collapsedPadding -= (1.0f + NotificationIconContainer.OVERFLOW_EARLY_AMOUNT)
524                     * mCollapsedIcons.getIconSize();
525         }
526         float padding = NotificationUtils.interpolate(collapsedPadding,
527                 mShelfIcons.getPaddingEnd(),
528                 openedAmount);
529         mShelfIcons.setActualPaddingEnd(padding);
530         float paddingStart = NotificationUtils.interpolate(start,
531                 mShelfIcons.getPaddingStart(), openedAmount);
532         mShelfIcons.setActualPaddingStart(paddingStart);
533         mShelfIcons.setOpenedAmount(openedAmount);
534         mShelfIcons.setVisualOverflowAdaption(mCollapsedIcons.getVisualOverflowAdaption());
535     }
536
537     public void setMaxLayoutHeight(int maxLayoutHeight) {
538         mMaxLayoutHeight = maxLayoutHeight;
539     }
540
541     /**
542      * @return the index of the notification at which the shelf visually resides
543      */
544     public int getNotGoneIndex() {
545         return mNotGoneIndex;
546     }
547
548     private void setHasItemsInStableShelf(boolean hasItemsInStableShelf) {
549         if (mHasItemsInStableShelf != hasItemsInStableShelf) {
550             mHasItemsInStableShelf = hasItemsInStableShelf;
551             updateInteractiveness();
552         }
553     }
554
555     /**
556      * @return whether the shelf has any icons in it when a potential animation has finished, i.e
557      *         if the current state would be applied right now
558      */
559     public boolean hasItemsInStableShelf() {
560         return mHasItemsInStableShelf;
561     }
562
563     public void setCollapsedIcons(NotificationIconContainer collapsedIcons) {
564         mCollapsedIcons = collapsedIcons;
565         mCollapsedIcons.addOnLayoutChangeListener(this);
566     }
567
568     public void setStatusBarState(int statusBarState) {
569         if (mStatusBarState != statusBarState) {
570             mStatusBarState = statusBarState;
571             updateInteractiveness();
572         }
573     }
574
575     private void updateInteractiveness() {
576         mInteractive = mStatusBarState == StatusBarState.KEYGUARD && mHasItemsInStableShelf
577                 && !mDark;
578         setClickable(mInteractive);
579         setFocusable(mInteractive);
580         setImportantForAccessibility(mInteractive ? View.IMPORTANT_FOR_ACCESSIBILITY_YES
581                 : View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
582     }
583
584     @Override
585     protected boolean isInteractive() {
586         return mInteractive;
587     }
588
589     public void setMaxShelfEnd(float maxShelfEnd) {
590         mMaxShelfEnd = maxShelfEnd;
591     }
592
593     public void setAnimationsEnabled(boolean enabled) {
594         mAnimationsEnabled = enabled;
595         mCollapsedIcons.setAnimationsEnabled(enabled);
596         if (!enabled) {
597             // we need to wait with enabling the animations until the first frame has passed
598             mShelfIcons.setAnimationsEnabled(false);
599         }
600     }
601
602     @Override
603     public boolean hasOverlappingRendering() {
604         return false;  // Shelf only uses alpha for transitions where the difference can't be seen.
605     }
606
607     @Override
608     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
609         super.onInitializeAccessibilityNodeInfo(info);
610         if (mInteractive) {
611             info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND);
612             AccessibilityNodeInfo.AccessibilityAction unlock
613                     = new AccessibilityNodeInfo.AccessibilityAction(
614                     AccessibilityNodeInfo.ACTION_CLICK,
615                     getContext().getString(R.string.accessibility_overflow_action));
616             info.addAction(unlock);
617         }
618     }
619
620     @Override
621     public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
622             int oldTop, int oldRight, int oldBottom) {
623         updateRelativeOffset();
624     }
625
626     private class ShelfState extends ExpandableViewState {
627         private float openedAmount;
628         private boolean hasItemsInStableShelf;
629         private float maxShelfEnd;
630
631         @Override
632         public void applyToView(View view) {
633             super.applyToView(view);
634             setMaxShelfEnd(maxShelfEnd);
635             setOpenedAmount(openedAmount);
636             updateAppearance();
637             setHasItemsInStableShelf(hasItemsInStableShelf);
638             mShelfIcons.setAnimationsEnabled(mAnimationsEnabled);
639         }
640
641         @Override
642         public void animateTo(View child, AnimationProperties properties) {
643             super.animateTo(child, properties);
644             setMaxShelfEnd(maxShelfEnd);
645             setOpenedAmount(openedAmount);
646             updateAppearance();
647             setHasItemsInStableShelf(hasItemsInStableShelf);
648             mShelfIcons.setAnimationsEnabled(mAnimationsEnabled);
649         }
650     }
651 }