blob: 44d666eb0d6515b8e87c000ee9fb192a3668a158 [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;
Selim Cinekd03518c2018-03-15 12:13:51 -070015import com.android.systemui.Dependency;
Anthony Chen83092c62016-01-11 17:00:36 -080016import com.android.systemui.R;
Selim Cinek281c2022016-10-13 19:14:43 -070017import com.android.systemui.statusbar.ExpandableNotificationRow;
Anthony Chen83092c62016-01-11 17:00:36 -080018import com.android.systemui.statusbar.NotificationData;
Selim Cinekd03518c2018-03-15 12:13:51 -070019import com.android.systemui.statusbar.NotificationEntryManager;
Selim Cinek281c2022016-10-13 19:14:43 -070020import com.android.systemui.statusbar.NotificationShelf;
Anthony Chen83092c62016-01-11 17:00:36 -080021import com.android.systemui.statusbar.StatusBarIconView;
22import com.android.systemui.statusbar.notification.NotificationUtils;
Jason Monkaa573e92017-01-27 17:00:29 -050023import com.android.systemui.statusbar.policy.DarkIconDispatcher;
24import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
Selim Cinek281c2022016-10-13 19:14:43 -070025import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
Anthony Chen83092c62016-01-11 17:00:36 -080026
27import java.util.ArrayList;
Selim Cinek281c2022016-10-13 19:14:43 -070028import java.util.function.Function;
Anthony Chen83092c62016-01-11 17:00:36 -080029
30/**
31 * A controller for the space in the status bar to the left of the system icons. This area is
32 * normally reserved for notifications.
33 */
Jason Monkaa573e92017-01-27 17:00:29 -050034public class NotificationIconAreaController implements DarkReceiver {
Anthony Chen83092c62016-01-11 17:00:36 -080035 private final NotificationColorUtil mNotificationColorUtil;
Selim Cinekd03518c2018-03-15 12:13:51 -070036 private final NotificationEntryManager mEntryManager;
37 private final Runnable mUpdateStatusBarIcons = this::updateStatusBarIcons;
Anthony Chen83092c62016-01-11 17:00:36 -080038
39 private int mIconSize;
40 private int mIconHPadding;
41 private int mIconTint = Color.WHITE;
42
Jason Monk2a6ea9c2017-01-26 11:14:51 -050043 private StatusBar mStatusBar;
Anthony Chen83092c62016-01-11 17:00:36 -080044 protected View mNotificationIconArea;
Selim Cinek281c2022016-10-13 19:14:43 -070045 private NotificationIconContainer mNotificationIcons;
Selim Cinek5b5beb012016-11-08 18:11:58 -080046 private NotificationIconContainer mShelfIcons;
Jorim Jaggi86905582016-02-09 21:36:09 -080047 private final Rect mTintArea = new Rect();
Selim Cinek281c2022016-10-13 19:14:43 -070048 private NotificationStackScrollLayout mNotificationScrollLayout;
49 private Context mContext;
Anthony Chen83092c62016-01-11 17:00:36 -080050
Jason Monk2a6ea9c2017-01-26 11:14:51 -050051 public NotificationIconAreaController(Context context, StatusBar statusBar) {
52 mStatusBar = statusBar;
Anthony Chen83092c62016-01-11 17:00:36 -080053 mNotificationColorUtil = NotificationColorUtil.getInstance(context);
Selim Cinek281c2022016-10-13 19:14:43 -070054 mContext = context;
Selim Cinekd03518c2018-03-15 12:13:51 -070055 mEntryManager = Dependency.get(NotificationEntryManager.class);
Anthony Chen83092c62016-01-11 17:00:36 -080056
57 initializeNotificationAreaViews(context);
58 }
59
Xiaohui Cheneb04a992016-03-22 14:58:03 -070060 protected View inflateIconArea(LayoutInflater inflater) {
61 return inflater.inflate(R.layout.notification_icon_area, null);
62 }
63
Anthony Chen83092c62016-01-11 17:00:36 -080064 /**
65 * Initializes the views that will represent the notification area.
66 */
67 protected void initializeNotificationAreaViews(Context context) {
Selim Cinek3e7592d2016-04-11 09:35:54 +080068 reloadDimens(context);
Anthony Chen83092c62016-01-11 17:00:36 -080069
70 LayoutInflater layoutInflater = LayoutInflater.from(context);
Xiaohui Cheneb04a992016-03-22 14:58:03 -070071 mNotificationIconArea = inflateIconArea(layoutInflater);
Selim Cinek281c2022016-10-13 19:14:43 -070072 mNotificationIcons = (NotificationIconContainer) mNotificationIconArea.findViewById(
73 R.id.notificationIcons);
Anthony Chen83092c62016-01-11 17:00:36 -080074
Jason Monkaa573e92017-01-27 17:00:29 -050075 mNotificationScrollLayout = mStatusBar.getNotificationScrollLayout();
76 }
77
78 public void setupShelf(NotificationShelf shelf) {
Selim Cinek5b5beb012016-11-08 18:11:58 -080079 mShelfIcons = shelf.getShelfIcons();
Selim Cinek49014f82016-11-04 14:55:30 -070080 shelf.setCollapsedIcons(mNotificationIcons);
Anthony Chen83092c62016-01-11 17:00:36 -080081 }
82
Selim Cinek3e7592d2016-04-11 09:35:54 +080083 public void onDensityOrFontScaleChanged(Context context) {
84 reloadDimens(context);
Selim Cinek0e8d77e2016-11-29 10:35:42 -080085 final FrameLayout.LayoutParams params = generateIconLayoutParams();
Selim Cinek3e7592d2016-04-11 09:35:54 +080086 for (int i = 0; i < mNotificationIcons.getChildCount(); i++) {
87 View child = mNotificationIcons.getChildAt(i);
88 child.setLayoutParams(params);
Selim Cinek9ef119c2017-03-01 15:13:36 -080089 }
90 for (int i = 0; i < mShelfIcons.getChildCount(); i++) {
91 View child = mShelfIcons.getChildAt(i);
Selim Cinek0e8d77e2016-11-29 10:35:42 -080092 child.setLayoutParams(params);
Selim Cinek3e7592d2016-04-11 09:35:54 +080093 }
94 }
95
96 @NonNull
Selim Cinek0e8d77e2016-11-29 10:35:42 -080097 private FrameLayout.LayoutParams generateIconLayoutParams() {
98 return new FrameLayout.LayoutParams(
Selim Cinek3e7592d2016-04-11 09:35:54 +080099 mIconSize + 2 * mIconHPadding, getHeight());
100 }
101
102 private void reloadDimens(Context context) {
103 Resources res = context.getResources();
104 mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
105 mIconHPadding = res.getDimensionPixelSize(R.dimen.status_bar_icon_padding);
106 }
107
Anthony Chen83092c62016-01-11 17:00:36 -0800108 /**
109 * Returns the view that represents the notification area.
110 */
111 public View getNotificationInnerAreaView() {
112 return mNotificationIconArea;
113 }
114
115 /**
Jason Monkaa573e92017-01-27 17:00:29 -0500116 * See {@link com.android.systemui.statusbar.policy.DarkIconDispatcher#setIconsDarkArea}.
117 * Sets the color that should be used to tint any icons in the notification area.
Jorim Jaggi86905582016-02-09 21:36:09 -0800118 *
119 * @param tintArea the area in which to tint the icons, specified in screen coordinates
Jason Monkaa573e92017-01-27 17:00:29 -0500120 * @param darkIntensity
Jorim Jaggi86905582016-02-09 21:36:09 -0800121 */
Jason Monkaa573e92017-01-27 17:00:29 -0500122 public void onDarkChanged(Rect tintArea, float darkIntensity, int iconTint) {
Jorim Jaggi86905582016-02-09 21:36:09 -0800123 if (tintArea == null) {
124 mTintArea.setEmpty();
125 } else {
126 mTintArea.set(tintArea);
127 }
Evan Lairddbeefe32018-04-03 16:52:41 -0400128 if (mNotificationIconArea != null) {
129 if (DarkIconDispatcher.isInArea(tintArea, mNotificationIconArea)) {
130 mIconTint = iconTint;
131 }
132 } else {
133 mIconTint = iconTint;
134 }
135
Anthony Chen83092c62016-01-11 17:00:36 -0800136 applyNotificationIconsTint();
137 }
138
Xiaohui Cheneb04a992016-03-22 14:58:03 -0700139 protected int getHeight() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500140 return mStatusBar.getStatusBarHeight();
Xiaohui Cheneb04a992016-03-22 14:58:03 -0700141 }
142
Selim Cinek17e1b692016-12-02 18:19:11 -0800143 protected boolean shouldShowNotificationIcon(NotificationData.Entry entry,
Selim Cinekb0dc61b2018-05-22 18:49:36 -0700144 boolean showAmbient, boolean hideDismissed, boolean hideRepliedMessages) {
Selim Cinekd03518c2018-03-15 12:13:51 -0700145 if (mEntryManager.getNotificationData().isAmbient(entry.key) && !showAmbient) {
Xiaohui Cheneb04a992016-03-22 14:58:03 -0700146 return false;
147 }
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500148 if (!StatusBar.isTopLevelChild(entry)) {
Xiaohui Cheneb04a992016-03-22 14:58:03 -0700149 return false;
150 }
151 if (entry.row.getVisibility() == View.GONE) {
152 return false;
153 }
Selim Cinekd03518c2018-03-15 12:13:51 -0700154 if (entry.row.isDismissed() && hideDismissed) {
155 return false;
156 }
Xiaohui Cheneb04a992016-03-22 14:58:03 -0700157
Selim Cinekb0dc61b2018-05-22 18:49:36 -0700158 if (hideRepliedMessages && entry.isLastMessageFromReply()) {
159 return false;
160 }
161
Julia Reynoldsc861a3d2018-02-15 10:34:49 -0500162 // showAmbient == show in shade but not shelf
Julia Reynoldsaa96cf32018-04-17 09:09:04 -0400163 if (!showAmbient && mEntryManager.getNotificationData().shouldSuppressStatusBar(entry)) {
Julia Reynoldsc861a3d2018-02-15 10:34:49 -0500164 return false;
165 }
166
Xiaohui Cheneb04a992016-03-22 14:58:03 -0700167 return true;
168 }
169
Anthony Chen83092c62016-01-11 17:00:36 -0800170 /**
171 * Updates the notifications with the given list of notifications to display.
172 */
Selim Cinekd03518c2018-03-15 12:13:51 -0700173 public void updateNotificationIcons() {
Anthony Chen83092c62016-01-11 17:00:36 -0800174
Selim Cinekd03518c2018-03-15 12:13:51 -0700175 updateStatusBarIcons();
176 updateIconsForLayout(entry -> entry.expandedIcon, mShelfIcons,
Selim Cinekb0dc61b2018-05-22 18:49:36 -0700177 NotificationShelf.SHOW_AMBIENT_ICONS, false /* hideDismissed */,
178 false /* hideRepliedMessages */);
Selim Cinek281c2022016-10-13 19:14:43 -0700179
180 applyNotificationIconsTint();
Selim Cinek281c2022016-10-13 19:14:43 -0700181 }
182
Selim Cinekb0dc61b2018-05-22 18:49:36 -0700183 public void updateStatusBarIcons() {
Selim Cinekd03518c2018-03-15 12:13:51 -0700184 updateIconsForLayout(entry -> entry.icon, mNotificationIcons,
Selim Cinekb0dc61b2018-05-22 18:49:36 -0700185 false /* showAmbient */, true /* hideDismissed */, true /* hideRepliedMessages */);
Selim Cinekd03518c2018-03-15 12:13:51 -0700186 }
187
Selim Cinek281c2022016-10-13 19:14:43 -0700188 /**
189 * Updates the notification icons for a host layout. This will ensure that the notification
190 * host layout will have the same icons like the ones in here.
Selim Cinek281c2022016-10-13 19:14:43 -0700191 * @param function A function to look up an icon view based on an entry
192 * @param hostLayout which layout should be updated
Selim Cinek17e1b692016-12-02 18:19:11 -0800193 * @param showAmbient should ambient notification icons be shown
Selim Cinekd03518c2018-03-15 12:13:51 -0700194 * @param hideDismissed should dismissed icons be hidden
Selim Cinekb0dc61b2018-05-22 18:49:36 -0700195 * @param hideRepliedMessages should messages that have been replied to be hidden
Selim Cinek281c2022016-10-13 19:14:43 -0700196 */
Selim Cinekd03518c2018-03-15 12:13:51 -0700197 private void updateIconsForLayout(Function<NotificationData.Entry, StatusBarIconView> function,
Selim Cinekb0dc61b2018-05-22 18:49:36 -0700198 NotificationIconContainer hostLayout, boolean showAmbient, boolean hideDismissed,
199 boolean hideRepliedMessages) {
Selim Cinek281c2022016-10-13 19:14:43 -0700200 ArrayList<StatusBarIconView> toShow = new ArrayList<>(
201 mNotificationScrollLayout.getChildCount());
Anthony Chen83092c62016-01-11 17:00:36 -0800202
203 // Filter out ambient notifications and notification children.
Selim Cinek281c2022016-10-13 19:14:43 -0700204 for (int i = 0; i < mNotificationScrollLayout.getChildCount(); i++) {
205 View view = mNotificationScrollLayout.getChildAt(i);
206 if (view instanceof ExpandableNotificationRow) {
207 NotificationData.Entry ent = ((ExpandableNotificationRow) view).getEntry();
Selim Cinekb0dc61b2018-05-22 18:49:36 -0700208 if (shouldShowNotificationIcon(ent, showAmbient, hideDismissed,
209 hideRepliedMessages)) {
Selim Cinek281c2022016-10-13 19:14:43 -0700210 toShow.add(function.apply(ent));
211 }
Anthony Chen83092c62016-01-11 17:00:36 -0800212 }
Anthony Chen83092c62016-01-11 17:00:36 -0800213 }
214
Selim Cinek72fc8db2017-06-06 18:07:47 -0700215 // In case we are changing the suppression of a group, the replacement shouldn't flicker
216 // and it should just be replaced instead. We therefore look for notifications that were
217 // just replaced by the child or vice-versa to suppress this.
Selim Cinek49014f82016-11-04 14:55:30 -0700218
Selim Cinek72fc8db2017-06-06 18:07:47 -0700219 ArrayMap<String, ArrayList<StatusBarIcon>> replacingIcons = new ArrayMap<>();
Anthony Chen83092c62016-01-11 17:00:36 -0800220 ArrayList<View> toRemove = new ArrayList<>();
Selim Cinek281c2022016-10-13 19:14:43 -0700221 for (int i = 0; i < hostLayout.getChildCount(); i++) {
222 View child = hostLayout.getChildAt(i);
Selim Cinek72fc8db2017-06-06 18:07:47 -0700223 if (!(child instanceof StatusBarIconView)) {
224 continue;
225 }
Anthony Chen83092c62016-01-11 17:00:36 -0800226 if (!toShow.contains(child)) {
Selim Cinek72fc8db2017-06-06 18:07:47 -0700227 boolean iconWasReplaced = false;
228 StatusBarIconView removedIcon = (StatusBarIconView) child;
229 String removedGroupKey = removedIcon.getNotification().getGroupKey();
230 for (int j = 0; j < toShow.size(); j++) {
231 StatusBarIconView candidate = toShow.get(j);
232 if (candidate.getSourceIcon().sameAs((removedIcon.getSourceIcon()))
233 && candidate.getNotification().getGroupKey().equals(removedGroupKey)) {
234 if (!iconWasReplaced) {
235 iconWasReplaced = true;
236 } else {
237 iconWasReplaced = false;
238 break;
239 }
240 }
241 }
242 if (iconWasReplaced) {
243 ArrayList<StatusBarIcon> statusBarIcons = replacingIcons.get(removedGroupKey);
244 if (statusBarIcons == null) {
245 statusBarIcons = new ArrayList<>();
246 replacingIcons.put(removedGroupKey, statusBarIcons);
247 }
248 statusBarIcons.add(removedIcon.getStatusBarIcon());
249 }
250 toRemove.add(removedIcon);
Anthony Chen83092c62016-01-11 17:00:36 -0800251 }
252 }
Selim Cinek72fc8db2017-06-06 18:07:47 -0700253 // removing all duplicates
254 ArrayList<String> duplicates = new ArrayList<>();
255 for (String key : replacingIcons.keySet()) {
256 ArrayList<StatusBarIcon> statusBarIcons = replacingIcons.get(key);
257 if (statusBarIcons.size() != 1) {
258 duplicates.add(key);
259 }
260 }
261 replacingIcons.removeAll(duplicates);
262 hostLayout.setReplacingIcons(replacingIcons);
Anthony Chen83092c62016-01-11 17:00:36 -0800263
264 final int toRemoveCount = toRemove.size();
265 for (int i = 0; i < toRemoveCount; i++) {
Selim Cinek281c2022016-10-13 19:14:43 -0700266 hostLayout.removeView(toRemove.get(i));
Anthony Chen83092c62016-01-11 17:00:36 -0800267 }
268
Selim Cinek0e8d77e2016-11-29 10:35:42 -0800269 final FrameLayout.LayoutParams params = generateIconLayoutParams();
Anthony Chen83092c62016-01-11 17:00:36 -0800270 for (int i = 0; i < toShow.size(); i++) {
Selim Cinekd03518c2018-03-15 12:13:51 -0700271 StatusBarIconView v = toShow.get(i);
Selim Cinek5b5beb012016-11-08 18:11:58 -0800272 // The view might still be transiently added if it was just removed and added again
273 hostLayout.removeTransientView(v);
Anthony Chen83092c62016-01-11 17:00:36 -0800274 if (v.getParent() == null) {
Selim Cinekd03518c2018-03-15 12:13:51 -0700275 if (hideDismissed) {
276 v.setOnDismissListener(mUpdateStatusBarIcons);
277 }
Selim Cinek281c2022016-10-13 19:14:43 -0700278 hostLayout.addView(v, i, params);
Anthony Chen83092c62016-01-11 17:00:36 -0800279 }
280 }
281
Selim Cinek5b5beb012016-11-08 18:11:58 -0800282 hostLayout.setChangingViewPositions(true);
Anthony Chen83092c62016-01-11 17:00:36 -0800283 // Re-sort notification icons
Selim Cinek281c2022016-10-13 19:14:43 -0700284 final int childCount = hostLayout.getChildCount();
Anthony Chen83092c62016-01-11 17:00:36 -0800285 for (int i = 0; i < childCount; i++) {
Selim Cinek281c2022016-10-13 19:14:43 -0700286 View actual = hostLayout.getChildAt(i);
Anthony Chen83092c62016-01-11 17:00:36 -0800287 StatusBarIconView expected = toShow.get(i);
288 if (actual == expected) {
289 continue;
290 }
Selim Cinek281c2022016-10-13 19:14:43 -0700291 hostLayout.removeView(expected);
292 hostLayout.addView(expected, i);
Anthony Chen83092c62016-01-11 17:00:36 -0800293 }
Selim Cinek5b5beb012016-11-08 18:11:58 -0800294 hostLayout.setChangingViewPositions(false);
Selim Cinek72fc8db2017-06-06 18:07:47 -0700295 hostLayout.setReplacingIcons(null);
Anthony Chen83092c62016-01-11 17:00:36 -0800296 }
297
298 /**
299 * Applies {@link #mIconTint} to the notification icons.
300 */
301 private void applyNotificationIconsTint() {
302 for (int i = 0; i < mNotificationIcons.getChildCount(); i++) {
Selim Cinek887da3c2017-10-06 13:37:32 -0700303 final StatusBarIconView iv = (StatusBarIconView) mNotificationIcons.getChildAt(i);
304 if (iv.getWidth() != 0) {
305 updateTintForIcon(iv);
306 } else {
307 iv.executeOnLayout(() -> updateTintForIcon(iv));
Anthony Chen83092c62016-01-11 17:00:36 -0800308 }
Anthony Chen83092c62016-01-11 17:00:36 -0800309 }
310 }
Lucas Dupin987f1932017-05-13 21:02:52 -0700311
Selim Cinek887da3c2017-10-06 13:37:32 -0700312 private void updateTintForIcon(StatusBarIconView v) {
313 boolean isPreL = Boolean.TRUE.equals(v.getTag(R.id.icon_is_pre_L));
314 int color = StatusBarIconView.NO_COLOR;
315 boolean colorize = !isPreL || NotificationUtils.isGrayscale(v, mNotificationColorUtil);
316 if (colorize) {
317 color = DarkIconDispatcher.getTint(mTintArea, v, mIconTint);
318 }
319 v.setStaticDrawableColor(color);
320 v.setDecorColor(mIconTint);
321 }
322
Lucas Dupin987f1932017-05-13 21:02:52 -0700323 public void setDark(boolean dark) {
324 mNotificationIcons.setDark(dark, false, 0);
325 mShelfIcons.setDark(dark, false, 0);
326 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800327
Selim Cinek332c23f2018-03-16 17:37:50 -0700328 public void showIconIsolated(StatusBarIconView icon, boolean animated) {
329 mNotificationIcons.showIconIsolated(icon, animated);
330 }
331
332 public void setIsolatedIconLocation(Rect iconDrawingRect, boolean requireStateUpdate) {
333 mNotificationIcons.setIsolatedIconLocation(iconDrawingRect, requireStateUpdate);
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800334 }
Anthony Chen83092c62016-01-11 17:00:36 -0800335}