blob: 40fe50fb93251979adee9238e3cc18a8a2901497 [file] [log] [blame]
Anthony Chen83092c62016-01-11 17:00:36 -08001package com.android.systemui.statusbar.phone;
2
3import android.content.Context;
Anthony Chen83092c62016-01-11 17:00:36 -08004import android.content.res.Resources;
5import android.graphics.Color;
Jorim Jaggi86905582016-02-09 21:36:09 -08006import android.graphics.Rect;
Selim Cinek3e7592d2016-04-11 09:35:54 +08007import android.support.annotation.NonNull;
Selim Cinek72fc8db2017-06-06 18:07:47 -07008import android.support.v4.util.ArrayMap;
Anthony Chen83092c62016-01-11 17:00:36 -08009import android.view.LayoutInflater;
10import android.view.View;
Selim Cinek0e8d77e2016-11-29 10:35:42 -080011import android.widget.FrameLayout;
Winsonc0d70582016-01-29 10:24:39 -080012
Selim Cinek72fc8db2017-06-06 18:07:47 -070013import com.android.internal.statusbar.StatusBarIcon;
Anthony Chen83092c62016-01-11 17:00:36 -080014import com.android.internal.util.NotificationColorUtil;
15import com.android.systemui.R;
Selim Cinek281c2022016-10-13 19:14:43 -070016import com.android.systemui.statusbar.ExpandableNotificationRow;
Anthony Chen83092c62016-01-11 17:00:36 -080017import com.android.systemui.statusbar.NotificationData;
Selim Cinek281c2022016-10-13 19:14:43 -070018import com.android.systemui.statusbar.NotificationShelf;
Anthony Chen83092c62016-01-11 17:00:36 -080019import com.android.systemui.statusbar.StatusBarIconView;
20import com.android.systemui.statusbar.notification.NotificationUtils;
Jason Monkaa573e92017-01-27 17:00:29 -050021import com.android.systemui.statusbar.policy.DarkIconDispatcher;
22import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
Selim Cinek281c2022016-10-13 19:14:43 -070023import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
Anthony Chen83092c62016-01-11 17:00:36 -080024
25import java.util.ArrayList;
Selim Cinek281c2022016-10-13 19:14:43 -070026import java.util.function.Function;
Anthony Chen83092c62016-01-11 17:00:36 -080027
28/**
29 * A controller for the space in the status bar to the left of the system icons. This area is
30 * normally reserved for notifications.
31 */
Jason Monkaa573e92017-01-27 17:00:29 -050032public class NotificationIconAreaController implements DarkReceiver {
Anthony Chen83092c62016-01-11 17:00:36 -080033 private final NotificationColorUtil mNotificationColorUtil;
34
35 private int mIconSize;
36 private int mIconHPadding;
37 private int mIconTint = Color.WHITE;
38
Jason Monk2a6ea9c2017-01-26 11:14:51 -050039 private StatusBar mStatusBar;
Anthony Chen83092c62016-01-11 17:00:36 -080040 protected View mNotificationIconArea;
Selim Cinek281c2022016-10-13 19:14:43 -070041 private NotificationIconContainer mNotificationIcons;
Selim Cinek5b5beb012016-11-08 18:11:58 -080042 private NotificationIconContainer mShelfIcons;
Jorim Jaggi86905582016-02-09 21:36:09 -080043 private final Rect mTintArea = new Rect();
Selim Cinek281c2022016-10-13 19:14:43 -070044 private NotificationStackScrollLayout mNotificationScrollLayout;
45 private Context mContext;
Anthony Chen83092c62016-01-11 17:00:36 -080046
Jason Monk2a6ea9c2017-01-26 11:14:51 -050047 public NotificationIconAreaController(Context context, StatusBar statusBar) {
48 mStatusBar = statusBar;
Anthony Chen83092c62016-01-11 17:00:36 -080049 mNotificationColorUtil = NotificationColorUtil.getInstance(context);
Selim Cinek281c2022016-10-13 19:14:43 -070050 mContext = context;
Anthony Chen83092c62016-01-11 17:00:36 -080051
52 initializeNotificationAreaViews(context);
53 }
54
Xiaohui Cheneb04a992016-03-22 14:58:03 -070055 protected View inflateIconArea(LayoutInflater inflater) {
56 return inflater.inflate(R.layout.notification_icon_area, null);
57 }
58
Anthony Chen83092c62016-01-11 17:00:36 -080059 /**
60 * Initializes the views that will represent the notification area.
61 */
62 protected void initializeNotificationAreaViews(Context context) {
Selim Cinek3e7592d2016-04-11 09:35:54 +080063 reloadDimens(context);
Anthony Chen83092c62016-01-11 17:00:36 -080064
65 LayoutInflater layoutInflater = LayoutInflater.from(context);
Xiaohui Cheneb04a992016-03-22 14:58:03 -070066 mNotificationIconArea = inflateIconArea(layoutInflater);
Selim Cinek281c2022016-10-13 19:14:43 -070067 mNotificationIcons = (NotificationIconContainer) mNotificationIconArea.findViewById(
68 R.id.notificationIcons);
Anthony Chen83092c62016-01-11 17:00:36 -080069
Jason Monkaa573e92017-01-27 17:00:29 -050070 mNotificationScrollLayout = mStatusBar.getNotificationScrollLayout();
71 }
72
73 public void setupShelf(NotificationShelf shelf) {
Selim Cinek5b5beb012016-11-08 18:11:58 -080074 mShelfIcons = shelf.getShelfIcons();
Selim Cinek49014f82016-11-04 14:55:30 -070075 shelf.setCollapsedIcons(mNotificationIcons);
Anthony Chen83092c62016-01-11 17:00:36 -080076 }
77
Selim Cinek3e7592d2016-04-11 09:35:54 +080078 public void onDensityOrFontScaleChanged(Context context) {
79 reloadDimens(context);
Selim Cinek0e8d77e2016-11-29 10:35:42 -080080 final FrameLayout.LayoutParams params = generateIconLayoutParams();
Selim Cinek3e7592d2016-04-11 09:35:54 +080081 for (int i = 0; i < mNotificationIcons.getChildCount(); i++) {
82 View child = mNotificationIcons.getChildAt(i);
83 child.setLayoutParams(params);
Selim Cinek9ef119c2017-03-01 15:13:36 -080084 }
85 for (int i = 0; i < mShelfIcons.getChildCount(); i++) {
86 View child = mShelfIcons.getChildAt(i);
Selim Cinek0e8d77e2016-11-29 10:35:42 -080087 child.setLayoutParams(params);
Selim Cinek3e7592d2016-04-11 09:35:54 +080088 }
89 }
90
91 @NonNull
Selim Cinek0e8d77e2016-11-29 10:35:42 -080092 private FrameLayout.LayoutParams generateIconLayoutParams() {
93 return new FrameLayout.LayoutParams(
Selim Cinek3e7592d2016-04-11 09:35:54 +080094 mIconSize + 2 * mIconHPadding, getHeight());
95 }
96
97 private void reloadDimens(Context context) {
98 Resources res = context.getResources();
99 mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
100 mIconHPadding = res.getDimensionPixelSize(R.dimen.status_bar_icon_padding);
101 }
102
Anthony Chen83092c62016-01-11 17:00:36 -0800103 /**
104 * Returns the view that represents the notification area.
105 */
106 public View getNotificationInnerAreaView() {
107 return mNotificationIconArea;
108 }
109
110 /**
Jason Monkaa573e92017-01-27 17:00:29 -0500111 * See {@link com.android.systemui.statusbar.policy.DarkIconDispatcher#setIconsDarkArea}.
112 * Sets the color that should be used to tint any icons in the notification area.
Jorim Jaggi86905582016-02-09 21:36:09 -0800113 *
114 * @param tintArea the area in which to tint the icons, specified in screen coordinates
Jason Monkaa573e92017-01-27 17:00:29 -0500115 * @param darkIntensity
Jorim Jaggi86905582016-02-09 21:36:09 -0800116 */
Jason Monkaa573e92017-01-27 17:00:29 -0500117 public void onDarkChanged(Rect tintArea, float darkIntensity, int iconTint) {
Jorim Jaggi86905582016-02-09 21:36:09 -0800118 if (tintArea == null) {
119 mTintArea.setEmpty();
120 } else {
121 mTintArea.set(tintArea);
122 }
Anthony Chen83092c62016-01-11 17:00:36 -0800123 mIconTint = iconTint;
Anthony Chen83092c62016-01-11 17:00:36 -0800124 applyNotificationIconsTint();
125 }
126
Xiaohui Cheneb04a992016-03-22 14:58:03 -0700127 protected int getHeight() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500128 return mStatusBar.getStatusBarHeight();
Xiaohui Cheneb04a992016-03-22 14:58:03 -0700129 }
130
Selim Cinek17e1b692016-12-02 18:19:11 -0800131 protected boolean shouldShowNotificationIcon(NotificationData.Entry entry,
132 NotificationData notificationData, boolean showAmbient) {
Jorim Jaggifabc7432017-05-15 02:40:05 +0200133 if (notificationData.isAmbient(entry.key) && !showAmbient) {
Xiaohui Cheneb04a992016-03-22 14:58:03 -0700134 return false;
135 }
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500136 if (!StatusBar.isTopLevelChild(entry)) {
Xiaohui Cheneb04a992016-03-22 14:58:03 -0700137 return false;
138 }
139 if (entry.row.getVisibility() == View.GONE) {
140 return false;
141 }
142
143 return true;
144 }
145
Anthony Chen83092c62016-01-11 17:00:36 -0800146 /**
147 * Updates the notifications with the given list of notifications to display.
148 */
149 public void updateNotificationIcons(NotificationData notificationData) {
Anthony Chen83092c62016-01-11 17:00:36 -0800150
Selim Cinek17e1b692016-12-02 18:19:11 -0800151 updateIconsForLayout(notificationData, entry -> entry.icon, mNotificationIcons,
152 false /* showAmbient */);
153 updateIconsForLayout(notificationData, entry -> entry.expandedIcon, mShelfIcons,
154 NotificationShelf.SHOW_AMBIENT_ICONS);
Selim Cinek281c2022016-10-13 19:14:43 -0700155
156 applyNotificationIconsTint();
Selim Cinek281c2022016-10-13 19:14:43 -0700157 }
158
159 /**
160 * Updates the notification icons for a host layout. This will ensure that the notification
161 * host layout will have the same icons like the ones in here.
162 *
163 * @param notificationData the notification data to look up which notifications are relevant
164 * @param function A function to look up an icon view based on an entry
165 * @param hostLayout which layout should be updated
Selim Cinek17e1b692016-12-02 18:19:11 -0800166 * @param showAmbient should ambient notification icons be shown
Selim Cinek281c2022016-10-13 19:14:43 -0700167 */
168 private void updateIconsForLayout(NotificationData notificationData,
169 Function<NotificationData.Entry, StatusBarIconView> function,
Selim Cinek17e1b692016-12-02 18:19:11 -0800170 NotificationIconContainer hostLayout, boolean showAmbient) {
Selim Cinek281c2022016-10-13 19:14:43 -0700171 ArrayList<StatusBarIconView> toShow = new ArrayList<>(
172 mNotificationScrollLayout.getChildCount());
Anthony Chen83092c62016-01-11 17:00:36 -0800173
174 // Filter out ambient notifications and notification children.
Selim Cinek281c2022016-10-13 19:14:43 -0700175 for (int i = 0; i < mNotificationScrollLayout.getChildCount(); i++) {
176 View view = mNotificationScrollLayout.getChildAt(i);
177 if (view instanceof ExpandableNotificationRow) {
178 NotificationData.Entry ent = ((ExpandableNotificationRow) view).getEntry();
Selim Cinek17e1b692016-12-02 18:19:11 -0800179 if (shouldShowNotificationIcon(ent, notificationData, showAmbient)) {
Selim Cinek281c2022016-10-13 19:14:43 -0700180 toShow.add(function.apply(ent));
181 }
Anthony Chen83092c62016-01-11 17:00:36 -0800182 }
Anthony Chen83092c62016-01-11 17:00:36 -0800183 }
184
Selim Cinek72fc8db2017-06-06 18:07:47 -0700185 // In case we are changing the suppression of a group, the replacement shouldn't flicker
186 // and it should just be replaced instead. We therefore look for notifications that were
187 // just replaced by the child or vice-versa to suppress this.
Selim Cinek49014f82016-11-04 14:55:30 -0700188
Selim Cinek72fc8db2017-06-06 18:07:47 -0700189 ArrayMap<String, ArrayList<StatusBarIcon>> replacingIcons = new ArrayMap<>();
Anthony Chen83092c62016-01-11 17:00:36 -0800190 ArrayList<View> toRemove = new ArrayList<>();
Selim Cinek281c2022016-10-13 19:14:43 -0700191 for (int i = 0; i < hostLayout.getChildCount(); i++) {
192 View child = hostLayout.getChildAt(i);
Selim Cinek72fc8db2017-06-06 18:07:47 -0700193 if (!(child instanceof StatusBarIconView)) {
194 continue;
195 }
Anthony Chen83092c62016-01-11 17:00:36 -0800196 if (!toShow.contains(child)) {
Selim Cinek72fc8db2017-06-06 18:07:47 -0700197 boolean iconWasReplaced = false;
198 StatusBarIconView removedIcon = (StatusBarIconView) child;
199 String removedGroupKey = removedIcon.getNotification().getGroupKey();
200 for (int j = 0; j < toShow.size(); j++) {
201 StatusBarIconView candidate = toShow.get(j);
202 if (candidate.getSourceIcon().sameAs((removedIcon.getSourceIcon()))
203 && candidate.getNotification().getGroupKey().equals(removedGroupKey)) {
204 if (!iconWasReplaced) {
205 iconWasReplaced = true;
206 } else {
207 iconWasReplaced = false;
208 break;
209 }
210 }
211 }
212 if (iconWasReplaced) {
213 ArrayList<StatusBarIcon> statusBarIcons = replacingIcons.get(removedGroupKey);
214 if (statusBarIcons == null) {
215 statusBarIcons = new ArrayList<>();
216 replacingIcons.put(removedGroupKey, statusBarIcons);
217 }
218 statusBarIcons.add(removedIcon.getStatusBarIcon());
219 }
220 toRemove.add(removedIcon);
Anthony Chen83092c62016-01-11 17:00:36 -0800221 }
222 }
Selim Cinek72fc8db2017-06-06 18:07:47 -0700223 // removing all duplicates
224 ArrayList<String> duplicates = new ArrayList<>();
225 for (String key : replacingIcons.keySet()) {
226 ArrayList<StatusBarIcon> statusBarIcons = replacingIcons.get(key);
227 if (statusBarIcons.size() != 1) {
228 duplicates.add(key);
229 }
230 }
231 replacingIcons.removeAll(duplicates);
232 hostLayout.setReplacingIcons(replacingIcons);
Anthony Chen83092c62016-01-11 17:00:36 -0800233
234 final int toRemoveCount = toRemove.size();
235 for (int i = 0; i < toRemoveCount; i++) {
Selim Cinek281c2022016-10-13 19:14:43 -0700236 hostLayout.removeView(toRemove.get(i));
Anthony Chen83092c62016-01-11 17:00:36 -0800237 }
238
Selim Cinek0e8d77e2016-11-29 10:35:42 -0800239 final FrameLayout.LayoutParams params = generateIconLayoutParams();
Anthony Chen83092c62016-01-11 17:00:36 -0800240 for (int i = 0; i < toShow.size(); i++) {
241 View v = toShow.get(i);
Selim Cinek5b5beb012016-11-08 18:11:58 -0800242 // The view might still be transiently added if it was just removed and added again
243 hostLayout.removeTransientView(v);
Anthony Chen83092c62016-01-11 17:00:36 -0800244 if (v.getParent() == null) {
Selim Cinek281c2022016-10-13 19:14:43 -0700245 hostLayout.addView(v, i, params);
Anthony Chen83092c62016-01-11 17:00:36 -0800246 }
247 }
248
Selim Cinek5b5beb012016-11-08 18:11:58 -0800249 hostLayout.setChangingViewPositions(true);
Anthony Chen83092c62016-01-11 17:00:36 -0800250 // Re-sort notification icons
Selim Cinek281c2022016-10-13 19:14:43 -0700251 final int childCount = hostLayout.getChildCount();
Anthony Chen83092c62016-01-11 17:00:36 -0800252 for (int i = 0; i < childCount; i++) {
Selim Cinek281c2022016-10-13 19:14:43 -0700253 View actual = hostLayout.getChildAt(i);
Anthony Chen83092c62016-01-11 17:00:36 -0800254 StatusBarIconView expected = toShow.get(i);
255 if (actual == expected) {
256 continue;
257 }
Selim Cinek281c2022016-10-13 19:14:43 -0700258 hostLayout.removeView(expected);
259 hostLayout.addView(expected, i);
Anthony Chen83092c62016-01-11 17:00:36 -0800260 }
Selim Cinek5b5beb012016-11-08 18:11:58 -0800261 hostLayout.setChangingViewPositions(false);
Selim Cinek72fc8db2017-06-06 18:07:47 -0700262 hostLayout.setReplacingIcons(null);
Anthony Chen83092c62016-01-11 17:00:36 -0800263 }
264
265 /**
266 * Applies {@link #mIconTint} to the notification icons.
267 */
268 private void applyNotificationIconsTint() {
269 for (int i = 0; i < mNotificationIcons.getChildCount(); i++) {
Selim Cinek887da3c2017-10-06 13:37:32 -0700270 final StatusBarIconView iv = (StatusBarIconView) mNotificationIcons.getChildAt(i);
271 if (iv.getWidth() != 0) {
272 updateTintForIcon(iv);
273 } else {
274 iv.executeOnLayout(() -> updateTintForIcon(iv));
Anthony Chen83092c62016-01-11 17:00:36 -0800275 }
Anthony Chen83092c62016-01-11 17:00:36 -0800276 }
277 }
Lucas Dupin987f1932017-05-13 21:02:52 -0700278
Selim Cinek887da3c2017-10-06 13:37:32 -0700279 private void updateTintForIcon(StatusBarIconView v) {
280 boolean isPreL = Boolean.TRUE.equals(v.getTag(R.id.icon_is_pre_L));
281 int color = StatusBarIconView.NO_COLOR;
282 boolean colorize = !isPreL || NotificationUtils.isGrayscale(v, mNotificationColorUtil);
283 if (colorize) {
284 color = DarkIconDispatcher.getTint(mTintArea, v, mIconTint);
285 }
286 v.setStaticDrawableColor(color);
287 v.setDecorColor(mIconTint);
288 }
289
Lucas Dupin987f1932017-05-13 21:02:52 -0700290 public void setDark(boolean dark) {
291 mNotificationIcons.setDark(dark, false, 0);
292 mShelfIcons.setDark(dark, false, 0);
293 }
Anthony Chen83092c62016-01-11 17:00:36 -0800294}