1 package com.android.systemui.statusbar.phone; 2 3 import android.content.Context; 4 import android.content.res.ColorStateList; 5 import android.content.res.Resources; 6 import android.graphics.Color; 7 import android.graphics.Rect; 8 import android.support.annotation.NonNull; 9 import android.view.LayoutInflater; 10 import android.view.View; 11 import android.widget.ImageView; 12 import android.widget.LinearLayout; 13 14 import com.android.internal.util.NotificationColorUtil; 15 import com.android.systemui.R; 16 import com.android.systemui.statusbar.NotificationData; 17 import com.android.systemui.statusbar.StatusBarIconView; 18 import com.android.systemui.statusbar.notification.NotificationUtils; 19 20 import java.util.ArrayList; 21 22 /** 23 * A controller for the space in the status bar to the left of the system icons. This area is 24 * normally reserved for notifications. 25 */ 26 public class NotificationIconAreaController { 27 private final NotificationColorUtil mNotificationColorUtil; 28 29 private int mIconSize; 30 private int mIconHPadding; 31 private int mIconTint = Color.WHITE; 32 33 private PhoneStatusBar mPhoneStatusBar; 34 protected View mNotificationIconArea; 35 private IconMerger mNotificationIcons; 36 private ImageView mMoreIcon; 37 private final Rect mTintArea = new Rect(); 38 39 public NotificationIconAreaController(Context context, PhoneStatusBar phoneStatusBar) { 40 mPhoneStatusBar = phoneStatusBar; 41 mNotificationColorUtil = NotificationColorUtil.getInstance(context); 42 43 initializeNotificationAreaViews(context); 44 } 45 46 protected View inflateIconArea(LayoutInflater inflater) { 47 return inflater.inflate(R.layout.notification_icon_area, null); 48 } 49 50 /** 51 * Initializes the views that will represent the notification area. 52 */ 53 protected void initializeNotificationAreaViews(Context context) { 54 reloadDimens(context); 55 56 LayoutInflater layoutInflater = LayoutInflater.from(context); 57 mNotificationIconArea = inflateIconArea(layoutInflater); 58 59 mNotificationIcons = 60 (IconMerger) mNotificationIconArea.findViewById(R.id.notificationIcons); 61 62 mMoreIcon = (ImageView) mNotificationIconArea.findViewById(R.id.moreIcon); 63 if (mMoreIcon != null) { 64 mMoreIcon.setImageTintList(ColorStateList.valueOf(mIconTint)); 65 mNotificationIcons.setOverflowIndicator(mMoreIcon); 66 } 67 } 68 69 public void onDensityOrFontScaleChanged(Context context) { 70 reloadDimens(context); 71 final LinearLayout.LayoutParams params = generateIconLayoutParams(); 72 for (int i = 0; i < mNotificationIcons.getChildCount(); i++) { 73 View child = mNotificationIcons.getChildAt(i); 74 child.setLayoutParams(params); 75 } 76 } 77 78 @NonNull 79 private LinearLayout.LayoutParams generateIconLayoutParams() { 80 return new LinearLayout.LayoutParams( 81 mIconSize + 2 * mIconHPadding, getHeight()); 82 } 83 84 private void reloadDimens(Context context) { 85 Resources res = context.getResources(); 86 mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size); 87 mIconHPadding = res.getDimensionPixelSize(R.dimen.status_bar_icon_padding); 88 } 89 90 /** 91 * Returns the view that represents the notification area. 92 */ 93 public View getNotificationInnerAreaView() { 94 return mNotificationIconArea; 95 } 96 97 /** 98 * See {@link StatusBarIconController#setIconsDarkArea}. 99 * 100 * @param tintArea the area in which to tint the icons, specified in screen coordinates 101 */ 102 public void setTintArea(Rect tintArea) { 103 if (tintArea == null) { 104 mTintArea.setEmpty(); 105 } else { 106 mTintArea.set(tintArea); 107 } 108 applyNotificationIconsTint(); 109 } 110 111 /** 112 * Sets the color that should be used to tint any icons in the notification area. If this 113 * method is not called, the default tint is {@link Color#WHITE}. 114 */ 115 public void setIconTint(int iconTint) { 116 mIconTint = iconTint; 117 if (mMoreIcon != null) { 118 mMoreIcon.setImageTintList(ColorStateList.valueOf(mIconTint)); 119 } 120 applyNotificationIconsTint(); 121 } 122 123 protected int getHeight() { 124 return mPhoneStatusBar.getStatusBarHeight(); 125 } 126 127 protected boolean shouldShowNotification(NotificationData.Entry entry, 128 NotificationData notificationData) { 129 if (notificationData.isAmbient(entry.key) 130 && !NotificationData.showNotificationEvenIfUnprovisioned(entry.notification)) { 131 return false; 132 } 133 if (!PhoneStatusBar.isTopLevelChild(entry)) { 134 return false; 135 } 136 if (entry.row.getVisibility() == View.GONE) { 137 return false; 138 } 139 140 return true; 141 } 142 143 /** 144 * Updates the notifications with the given list of notifications to display. 145 */ 146 public void updateNotificationIcons(NotificationData notificationData) { 147 final LinearLayout.LayoutParams params = generateIconLayoutParams(); 148 149 ArrayList<NotificationData.Entry> activeNotifications = 150 notificationData.getActiveNotifications(); 151 final int size = activeNotifications.size(); 152 ArrayList<StatusBarIconView> toShow = new ArrayList<>(size); 153 154 // Filter out ambient notifications and notification children. 155 for (int i = 0; i < size; i++) { 156 NotificationData.Entry ent = activeNotifications.get(i); 157 if (shouldShowNotification(ent, notificationData)) { 158 toShow.add(ent.icon); 159 } 160 } 161 162 ArrayList<View> toRemove = new ArrayList<>(); 163 for (int i = 0; i < mNotificationIcons.getChildCount(); i++) { 164 View child = mNotificationIcons.getChildAt(i); 165 if (!toShow.contains(child)) { 166 toRemove.add(child); 167 } 168 } 169 170 final int toRemoveCount = toRemove.size(); 171 for (int i = 0; i < toRemoveCount; i++) { 172 mNotificationIcons.removeView(toRemove.get(i)); 173 } 174 175 for (int i = 0; i < toShow.size(); i++) { 176 View v = toShow.get(i); 177 if (v.getParent() == null) { 178 mNotificationIcons.addView(v, i, params); 179 } 180 } 181 182 // Re-sort notification icons 183 final int childCount = mNotificationIcons.getChildCount(); 184 for (int i = 0; i < childCount; i++) { 185 View actual = mNotificationIcons.getChildAt(i); 186 StatusBarIconView expected = toShow.get(i); 187 if (actual == expected) { 188 continue; 189 } 190 mNotificationIcons.removeView(expected); 191 mNotificationIcons.addView(expected, i); 192 } 193 194 applyNotificationIconsTint(); 195 } 196 197 /** 198 * Applies {@link #mIconTint} to the notification icons. 199 */ 200 private void applyNotificationIconsTint() { 201 for (int i = 0; i < mNotificationIcons.getChildCount(); i++) { 202 StatusBarIconView v = (StatusBarIconView) mNotificationIcons.getChildAt(i); 203 boolean isPreL = Boolean.TRUE.equals(v.getTag(R.id.icon_is_pre_L)); 204 boolean colorize = !isPreL || NotificationUtils.isGrayscale(v, mNotificationColorUtil); 205 if (colorize) { 206 v.setImageTintList(ColorStateList.valueOf( 207 StatusBarIconController.getTint(mTintArea, v, mIconTint))); 208 } 209 } 210 } 211 } 212