Introduced the Notification shelf

Started the implementation of a new notification
scrolling model that dynamically transforms notifications
into a new icon shelf.

Test: Add notifications, scroll!
Bug: 32437839
Change-Id: Ic28cc1c82f2cda2ffa7a312648903b5bc2408820
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/IconMerger.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/IconMerger.java
deleted file mode 100644
index f86badb..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/IconMerger.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.LinearLayout;
-
-import com.android.systemui.R;
-
-public class IconMerger extends LinearLayout {
-    private static final String TAG = "IconMerger";
-    private static final boolean DEBUG = false;
-
-    private int mIconSize;
-    private int mIconHPadding;
-
-    private View mMoreView;
-
-    public IconMerger(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        reloadDimens();
-        if (DEBUG) {
-            setBackgroundColor(0x800099FF);
-        }
-    }
-
-    private void reloadDimens() {
-        Resources res = mContext.getResources();
-        mIconSize = res.getDimensionPixelSize(R.dimen.status_bar_icon_size);
-        mIconHPadding = res.getDimensionPixelSize(R.dimen.status_bar_icon_padding);
-    }
-
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        reloadDimens();
-    }
-
-    public void setOverflowIndicator(View v) {
-        mMoreView = v;
-    }
-
-    private int getFullIconWidth() {
-        return mIconSize + 2 * mIconHPadding;
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        // we need to constrain this to an integral multiple of our children
-        int width = getMeasuredWidth();
-        setMeasuredDimension(width - (width % getFullIconWidth()), getMeasuredHeight());
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        super.onLayout(changed, l, t, r, b);
-        checkOverflow(r - l);
-    }
-
-    private void checkOverflow(int width) {
-        if (mMoreView == null) return;
-
-        final int N = getChildCount();
-        int visibleChildren = 0;
-        for (int i=0; i<N; i++) {
-            if (getChildAt(i).getVisibility() != GONE) visibleChildren++;
-        }
-        final boolean overflowShown = (mMoreView.getVisibility() == View.VISIBLE);
-        // let's assume we have one more slot if the more icon is already showing
-        if (overflowShown) visibleChildren --;
-        final boolean moreRequired = visibleChildren * getFullIconWidth() > width;
-        if (moreRequired != overflowShown) {
-            post(new Runnable() {
-                @Override
-                public void run() {
-                    mMoreView.setVisibility(moreRequired ? View.VISIBLE : View.GONE);
-                }
-            });
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 784cb48..70beac8ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -80,7 +80,7 @@
         mClockYFractionMin = res.getFraction(R.fraction.keyguard_clock_y_fraction_min, 1, 1);
         mClockYFractionMax = res.getFraction(R.fraction.keyguard_clock_y_fraction_max, 1, 1);
         mMoreCardNotificationAmount =
-                (float) res.getDimensionPixelSize(R.dimen.notification_summary_height) /
+                (float) res.getDimensionPixelSize(R.dimen.notification_shelf_height) /
                         res.getDimensionPixelSize(R.dimen.notification_min_height);
         mDensity = res.getDisplayMetrics().density;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index cbaab14..72e84a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -8,16 +8,20 @@
 import android.support.annotation.NonNull;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.widget.ImageView;
+import android.view.ViewGroup;
 import android.widget.LinearLayout;
 
 import com.android.internal.util.NotificationColorUtil;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
 import java.util.ArrayList;
+import java.util.function.Function;
 
 /**
  * A controller for the space in the status bar to the left of the system icons. This area is
@@ -32,13 +36,17 @@
 
     private PhoneStatusBar mPhoneStatusBar;
     protected View mNotificationIconArea;
-    private IconMerger mNotificationIcons;
-    private ImageView mMoreIcon;
+    private NotificationShelf mNotificationIconAreaScroller;
+    private NotificationIconContainer mNotificationIcons;
+    private NotificationIconContainer mNotificationIconsScroller;
     private final Rect mTintArea = new Rect();
+    private NotificationStackScrollLayout mNotificationScrollLayout;
+    private Context mContext;
 
     public NotificationIconAreaController(Context context, PhoneStatusBar phoneStatusBar) {
         mPhoneStatusBar = phoneStatusBar;
         mNotificationColorUtil = NotificationColorUtil.getInstance(context);
+        mContext = context;
 
         initializeNotificationAreaViews(context);
     }
@@ -55,15 +63,13 @@
 
         LayoutInflater layoutInflater = LayoutInflater.from(context);
         mNotificationIconArea = inflateIconArea(layoutInflater);
+        mNotificationIcons = (NotificationIconContainer) mNotificationIconArea.findViewById(
+                R.id.notificationIcons);
 
-        mNotificationIcons =
-                (IconMerger) mNotificationIconArea.findViewById(R.id.notificationIcons);
+        mNotificationIconAreaScroller = mPhoneStatusBar.getNotificationShelf();
+        mNotificationIconsScroller = mNotificationIconAreaScroller.getNotificationIconContainer();
 
-        mMoreIcon = (ImageView) mNotificationIconArea.findViewById(R.id.moreIcon);
-        if (mMoreIcon != null) {
-            mMoreIcon.setImageTintList(ColorStateList.valueOf(mIconTint));
-            mNotificationIcons.setOverflowIndicator(mMoreIcon);
-        }
+        mNotificationScrollLayout = mPhoneStatusBar.getNotificationScrollLayout();
     }
 
     public void onDensityOrFontScaleChanged(Context context) {
@@ -114,9 +120,7 @@
      */
     public void setIconTint(int iconTint) {
         mIconTint = iconTint;
-        if (mMoreIcon != null) {
-            mMoreIcon.setImageTintList(ColorStateList.valueOf(mIconTint));
-        }
+        mNotificationIcons.setIconTint(mIconTint);
         applyNotificationIconsTint();
     }
 
@@ -144,24 +148,54 @@
      * Updates the notifications with the given list of notifications to display.
      */
     public void updateNotificationIcons(NotificationData notificationData) {
-        final LinearLayout.LayoutParams params = generateIconLayoutParams();
 
-        ArrayList<NotificationData.Entry> activeNotifications =
-                notificationData.getActiveNotifications();
-        final int size = activeNotifications.size();
-        ArrayList<StatusBarIconView> toShow = new ArrayList<>(size);
+        updateIconsForLayout(notificationData, entry -> entry.icon, mNotificationIcons);
+        updateIconsForLayout(notificationData, entry -> entry.expandedIcon,
+                mNotificationIconsScroller);
+
+        applyNotificationIconsTint();
+        ArrayList<NotificationData.Entry> activeNotifications
+                = notificationData.getActiveNotifications();
+        for (int i = 0; i < activeNotifications.size(); i++) {
+            NotificationData.Entry entry = activeNotifications.get(i);
+            boolean isPreL = Boolean.TRUE.equals(entry.expandedIcon.getTag(R.id.icon_is_pre_L));
+            boolean colorize = !isPreL
+                    || NotificationUtils.isGrayscale(entry.expandedIcon, mNotificationColorUtil);
+            if (colorize) {
+                int color = entry.getContrastedColor(mContext);
+                entry.expandedIcon.setImageTintList(ColorStateList.valueOf(color));
+            }
+        }
+    }
+
+    /**
+     * Updates the notification icons for a host layout. This will ensure that the notification
+     * host layout will have the same icons like the ones in here.
+     *
+     * @param notificationData the notification data to look up which notifications are relevant
+     * @param function A function to look up an icon view based on an entry
+     * @param hostLayout which layout should be updated
+     */
+    private void updateIconsForLayout(NotificationData notificationData,
+            Function<NotificationData.Entry, StatusBarIconView> function,
+            ViewGroup hostLayout) {
+        ArrayList<StatusBarIconView> toShow = new ArrayList<>(
+                mNotificationScrollLayout.getChildCount());
 
         // Filter out ambient notifications and notification children.
-        for (int i = 0; i < size; i++) {
-            NotificationData.Entry ent = activeNotifications.get(i);
-            if (shouldShowNotification(ent, notificationData)) {
-                toShow.add(ent.icon);
+        for (int i = 0; i < mNotificationScrollLayout.getChildCount(); i++) {
+            View view = mNotificationScrollLayout.getChildAt(i);
+            if (view instanceof ExpandableNotificationRow) {
+                NotificationData.Entry ent = ((ExpandableNotificationRow) view).getEntry();
+                if (shouldShowNotification(ent, notificationData)) {
+                    toShow.add(function.apply(ent));
+                }
             }
         }
 
         ArrayList<View> toRemove = new ArrayList<>();
-        for (int i = 0; i < mNotificationIcons.getChildCount(); i++) {
-            View child = mNotificationIcons.getChildAt(i);
+        for (int i = 0; i < hostLayout.getChildCount(); i++) {
+            View child = hostLayout.getChildAt(i);
             if (!toShow.contains(child)) {
                 toRemove.add(child);
             }
@@ -169,29 +203,28 @@
 
         final int toRemoveCount = toRemove.size();
         for (int i = 0; i < toRemoveCount; i++) {
-            mNotificationIcons.removeView(toRemove.get(i));
+            hostLayout.removeView(toRemove.get(i));
         }
 
+        final LinearLayout.LayoutParams params = generateIconLayoutParams();
         for (int i = 0; i < toShow.size(); i++) {
             View v = toShow.get(i);
             if (v.getParent() == null) {
-                mNotificationIcons.addView(v, i, params);
+                hostLayout.addView(v, i, params);
             }
         }
 
         // Re-sort notification icons
-        final int childCount = mNotificationIcons.getChildCount();
+        final int childCount = hostLayout.getChildCount();
         for (int i = 0; i < childCount; i++) {
-            View actual = mNotificationIcons.getChildAt(i);
+            View actual = hostLayout.getChildAt(i);
             StatusBarIconView expected = toShow.get(i);
             if (actual == expected) {
                 continue;
             }
-            mNotificationIcons.removeView(expected);
-            mNotificationIcons.addView(expected, i);
+            hostLayout.removeView(expected);
+            hostLayout.addView(expected, i);
         }
-
-        applyNotificationIconsTint();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
new file mode 100644
index 0000000..bf7886d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.systemui.statusbar.AlphaOptimizedFrameLayout;
+import com.android.systemui.statusbar.stack.ViewState;
+
+import java.util.WeakHashMap;
+
+/**
+ * A container for notification icons. It handles overflowing icons properly and positions them
+ * correctly on the screen.
+ */
+public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
+    private static final String TAG = "NotificationIconContainer";
+
+    private boolean mShowAllIcons = true;
+    private int mIconTint;
+    private WeakHashMap<View, ViewState> mIconStates = new WeakHashMap<>();
+
+    public NotificationIconContainer(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        float centerY = getHeight() / 2.0f;
+        // we layout all our children on the left at the top
+        for (int i = 0; i < getChildCount(); i++) {
+            View child = getChildAt(i);
+            // We need to layout all children even the GONE ones, such that the heights are
+            // calculated correctly as they are used to calculate how many we can fit on the screen
+            int width = child.getMeasuredWidth();
+            int height = child.getMeasuredHeight();
+            int top = (int) (centerY - height / 2.0f);
+            child.layout(0, top, width, top + height);
+        }
+        if (mShowAllIcons) {
+            resetViewStates(mIconStates);
+            calculateIconStates(getChildCount());
+            applyIconStates(mIconStates);
+        }
+    }
+
+    public void applyIconStates(WeakHashMap<View, ViewState> iconStates) {
+        for (int i = 0; i < getChildCount(); i++) {
+            View child = getChildAt(i);
+            ViewState childState = iconStates.get(child);
+            if (childState != null) {
+                childState.applyToView(child);
+            }
+        }
+    }
+
+    @Override
+    public void onViewAdded(View child) {
+        super.onViewAdded(child);
+        mIconStates.put(child, new ViewState());
+    }
+
+    @Override
+    public void onViewRemoved(View child) {
+        super.onViewRemoved(child);
+        mIconStates.remove(child);
+    }
+
+    public void setIconTint(int iconTint) {
+        mIconTint = iconTint;
+    }
+
+    public void resetViewStates(WeakHashMap<View, ViewState> viewStates) {
+        for (int i = 0; i < getChildCount(); i++) {
+            View view = getChildAt(i);
+            ViewState iconState = mIconStates.get(view);
+            iconState.initFrom(view);
+        }
+    }
+
+    /**
+     * Gets a new state based on the number of visible icons starting from the right.
+     * If this is not a whole number, the fraction means by how much the icon is appearing.
+     */
+    public WeakHashMap<View, ViewState> calculateIconStates(float numberOfVisibleIcons) {
+        int childCount = getChildCount();
+        float visibleIconStart = childCount - numberOfVisibleIcons;
+        int firstIconIndex = (int) visibleIconStart;
+        float translationX = 0.0f;
+        for (int i = 0; i < childCount; i++) {
+            View view = getChildAt(i);
+            ViewState iconState = mIconStates.get(view);
+            if (i >= firstIconIndex) {
+                iconState.xTranslation = translationX;
+                float appearAmount = 1.0f;
+                if (i == firstIconIndex) {
+                    appearAmount = 1.0f - (visibleIconStart - firstIconIndex);
+                }
+                translationX += appearAmount * view.getWidth();
+            }
+        }
+        return mIconStates;
+    }
+
+    public WeakHashMap<View, ViewState> getIconStates() {
+        return mIconStates;
+    }
+
+    /**
+     * Sets whether the layout should always show all icons.
+     * If this is true, the icon positions will be updated on layout.
+     * If this if false, the layout is managed from the outside and layouting won't trigger a
+     * repositioning of the icons.
+     */
+    public void setShowAllIcons(boolean showAllIcons) {
+        mShowAllIcons = showAllIcons;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 068631d..dc30999 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -404,10 +404,11 @@
         int notificationPadding = Math.max(1, getResources().getDimensionPixelSize(
                 R.dimen.notification_divider_height));
         final int overflowheight = getResources().getDimensionPixelSize(
-                R.dimen.notification_summary_height);
-        float bottomStackSize = mNotificationStackScroller.getKeyguardBottomStackSize();
+                R.dimen.notification_shelf_height);
+        float shelfSize = mNotificationStackScroller.getNotificationShelf().getIntrinsicHeight()
+                + notificationPadding;
         float availableSpace = mNotificationStackScroller.getHeight() - minPadding - overflowheight
-                - bottomStackSize;
+                - shelfSize;
         int count = 0;
         for (int i = 0; i < mNotificationStackScroller.getChildCount(); i++) {
             ExpandableView child = (ExpandableView) mNotificationStackScroller.getChildAt(i);
@@ -429,6 +430,16 @@
             availableSpace -= child.getMinHeight() + notificationPadding;
             if (availableSpace >= 0 && count < maximum) {
                 count++;
+            } else if (availableSpace > -shelfSize) {
+                // if we are exactly the last view, then we can show us still!
+                for (int j = i + 1; j < mNotificationStackScroller.getChildCount(); j++) {
+                    if (mNotificationStackScroller.getChildAt(j)
+                            instanceof ExpandableNotificationRow) {
+                        return count;
+                    }
+                }
+                count++;
+                return count;
             } else {
                 return count;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 9b93527..b847cdc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -164,7 +164,7 @@
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.NotificationData.Entry;
-import com.android.systemui.statusbar.NotificationOverflowContainer;
+import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.SignalClusterView;
@@ -659,10 +659,12 @@
         array.clear();
     }
 
-    private final View.OnClickListener mOverflowClickListener = new View.OnClickListener() {
+    private final View.OnClickListener mShelfClickListener = new View.OnClickListener() {
         @Override
         public void onClick(View v) {
-            goToLockedShade(null);
+            if (mState == StatusBarState.KEYGUARD) {
+                goToLockedShade(null);
+            }
         }
     };
     private HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>> mTmpChildOrderMap
@@ -812,7 +814,7 @@
         mStackScroller.setHeadsUpManager(mHeadsUpManager);
         mGroupManager.setOnGroupChangeListener(mStackScroller);
 
-        inflateOverflowContainer();
+        inflateShelf();
         inflateEmptyShadeView();
         inflateDismissView();
         mExpandedContents = mStackScroller;
@@ -1049,13 +1051,13 @@
         return new BatteryControllerImpl(mContext);
     }
 
-    private void inflateOverflowContainer() {
-        mKeyguardIconOverflowContainer =
-                (NotificationOverflowContainer) LayoutInflater.from(mContext).inflate(
-                        R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false);
-        mKeyguardIconOverflowContainer.setOnActivatedListener(this);
-        mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener);
-        mStackScroller.setOverflowContainer(mKeyguardIconOverflowContainer);
+    private void inflateShelf() {
+        mNotificationShelf =
+                (NotificationShelf) LayoutInflater.from(mContext).inflate(
+                        R.layout.status_bar_notification_icon_container, mStackScroller, false);
+        mNotificationShelf.setOnActivatedListener(this);
+        mNotificationShelf.setOnClickListener(mShelfClickListener);
+        mStackScroller.setShelf(mNotificationShelf);
     }
 
     @Override
@@ -1072,7 +1074,7 @@
         updateClearAll();
         inflateEmptyShadeView();
         updateEmptyShadeView();
-        inflateOverflowContainer();
+        inflateShelf();
         mStatusBarKeyguardViewManager.onDensityOrFontScaleChanged();
         mUserInfoController.onDensityOrFontScaleChanged();
         if (mUserSwitcherController != null) {
@@ -1866,7 +1868,7 @@
         mTmpChildOrderMap.clear();
 
         updateRowStates();
-        updateSpeedbump();
+        updateShelfIndex();
         updateClearAll();
         updateEmptyShadeView();
 
@@ -1987,8 +1989,8 @@
         mNotificationPanel.setShadeEmpty(showEmptyShade);
     }
 
-    private void updateSpeedbump() {
-        int speedbumpIndex = -1;
+    private void updateShelfIndex() {
+        int shelfIndex = -1;
         int currentIndex = 0;
         final int N = mStackScroller.getChildCount();
         for (int i = 0; i < N; i++) {
@@ -1998,12 +2000,12 @@
             }
             ExpandableNotificationRow row = (ExpandableNotificationRow) view;
             if (mNotificationData.isAmbient(row.getStatusBarNotification().getKey())) {
-                speedbumpIndex = currentIndex;
+                shelfIndex = currentIndex;
                 break;
             }
             currentIndex++;
         }
-        mStackScroller.updateSpeedBumpIndex(speedbumpIndex);
+        mStackScroller.updateShelfIndex(shelfIndex);
     }
 
     public static boolean isTopLevelChild(Entry entry) {
@@ -2694,6 +2696,14 @@
         mFalsingManager.onScreenOff();
     }
 
+    public NotificationShelf getNotificationShelf() {
+        return mNotificationShelf;
+    }
+
+    public NotificationStackScrollLayout getNotificationScrollLayout() {
+        return mStackScroller;
+    }
+
     public boolean isPulsing() {
         return mDozeScrimController.isPulsing();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index dbe7f96..a948a08 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -44,6 +44,7 @@
 import com.android.systemui.R;
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.SignalClusterView;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.tuner.TunerService;
@@ -74,6 +75,7 @@
 
     private NotificationIconAreaController mNotificationIconAreaController;
     private View mNotificationIconAreaInner;
+    private NotificationShelf mNotificationShelf;
 
     private BatteryMeterView mBatteryMeterView;
     private BatteryMeterView mBatteryMeterViewKeyguard;
@@ -123,6 +125,7 @@
         mStatusIcons = (LinearLayout) statusBar.findViewById(R.id.statusIcons);
         mSignalCluster = (SignalClusterView) statusBar.findViewById(R.id.signal_cluster);
 
+        mNotificationShelf = phoneStatusBar.getNotificationShelf();
         mNotificationIconAreaController = SystemUIFactory.getInstance()
                 .createNotificationIconAreaController(context, phoneStatusBar);
         mNotificationIconAreaInner =