blob: 264655c773011710bcc5adc7a6042404cd993883 [file] [log] [blame]
Chris Wren51c75102013-07-16 20:49:17 -04001/*
2 * Copyright (C) 2013 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
17package com.android.systemui.statusbar;
18
Selim Cineka6c6bfb2015-10-29 16:27:08 -070019import android.app.Notification;
Chris Wren51c75102013-07-16 20:49:17 -040020import android.content.Context;
Selim Cinekcab4a602014-09-03 14:47:57 +020021import android.graphics.drawable.AnimatedVectorDrawable;
22import android.graphics.drawable.AnimationDrawable;
Selim Cinekb5605e52015-02-20 18:21:41 +010023import android.graphics.drawable.ColorDrawable;
Selim Cinekcab4a602014-09-03 14:47:57 +020024import android.graphics.drawable.Drawable;
Selim Cinekda42d652015-12-04 15:51:16 -080025import android.os.Build;
Jorim Jaggib1cd3c12014-09-08 19:55:17 +020026import android.service.notification.StatusBarNotification;
Chris Wren51c75102013-07-16 20:49:17 -040027import android.util.AttributeSet;
Selim Cinek01af3342016-02-09 19:25:31 -080028import android.view.LayoutInflater;
Selim Cinek6183d122016-01-14 18:48:41 -080029import android.view.MotionEvent;
Selim Cinekeaa29ca2015-11-23 13:51:13 -080030import android.view.NotificationHeaderView;
Dan Sandlera5e0f412014-01-23 15:11:54 -050031import android.view.View;
Jorim Jaggib1cd3c12014-09-08 19:55:17 +020032import android.view.ViewStub;
Jorim Jaggife40f7d2014-04-28 15:20:04 +020033import android.view.accessibility.AccessibilityEvent;
Selim Cinek98713a42015-09-21 15:47:20 +020034import android.widget.Chronometer;
Selim Cinekcab4a602014-09-03 14:47:57 +020035import android.widget.ImageView;
Selim Cinekeaa29ca2015-11-23 13:51:13 -080036import android.widget.RemoteViews;
Selim Cinekb5605e52015-02-20 18:21:41 +010037
Dan Sandlera5e0f412014-01-23 15:11:54 -050038import com.android.systemui.R;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070039import com.android.systemui.classifier.FalsingManager;
Selim Cinek0ffbda62016-01-01 20:29:12 +010040import com.android.systemui.statusbar.notification.NotificationViewWrapper;
Selim Cinekb5605e52015-02-20 18:21:41 +010041import com.android.systemui.statusbar.phone.NotificationGroupManager;
Selim Cinek31aada42015-12-18 17:51:15 -080042import com.android.systemui.statusbar.policy.HeadsUpManager;
Selim Cinekb5605e52015-02-20 18:21:41 +010043import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
44import com.android.systemui.statusbar.stack.StackScrollState;
45import com.android.systemui.statusbar.stack.StackStateAnimator;
46import com.android.systemui.statusbar.stack.StackViewState;
47
48import java.util.List;
Dan Sandlera5e0f412014-01-23 15:11:54 -050049
Jorim Jaggi4222d9a2014-04-23 16:13:15 +020050public class ExpandableNotificationRow extends ActivatableNotificationView {
Selim Cinekb5605e52015-02-20 18:21:41 +010051
52 private static final int DEFAULT_DIVIDER_ALPHA = 0x29;
53 private static final int COLORED_DIVIDER_ALPHA = 0x7B;
Selim Cinek01af3342016-02-09 19:25:31 -080054 private int mNotificationMinHeightLegacy;
55 private int mMaxHeadsUpHeightLegacy;
56 private int mMaxHeadsUpHeight;
57 private int mNotificationMinHeight;
58 private int mNotificationMaxHeight;
Chris Wren51c75102013-07-16 20:49:17 -040059
Selim Cinek1685e632014-04-08 02:27:49 +020060 /** Does this row contain layouts that can adapt to row expansion */
Chris Wren51c75102013-07-16 20:49:17 -040061 private boolean mExpandable;
Selim Cinek1685e632014-04-08 02:27:49 +020062 /** Has the user actively changed the expansion state of this row */
63 private boolean mHasUserChangedExpansion;
64 /** If {@link #mHasUserChangedExpansion}, has the user expanded this row */
Chris Wren51c75102013-07-16 20:49:17 -040065 private boolean mUserExpanded;
Selim Cinek31aada42015-12-18 17:51:15 -080066
67 /**
68 * Has this notification been expanded while it was pinned
69 */
70 private boolean mExpandedWhenPinned;
Selim Cinek1685e632014-04-08 02:27:49 +020071 /** Is the user touching this row */
Chris Wren51c75102013-07-16 20:49:17 -040072 private boolean mUserLocked;
Selim Cinek1685e632014-04-08 02:27:49 +020073 /** Are we showing the "public" version */
Dan Sandlera5e0f412014-01-23 15:11:54 -050074 private boolean mShowingPublic;
Jorim Jaggiae441282014-08-01 02:45:18 +020075 private boolean mSensitive;
76 private boolean mShowingPublicInitialized;
Selim Cinek60122be2015-04-15 18:16:50 -070077 private boolean mHideSensitiveForIntrinsicHeight;
Chris Wren51c75102013-07-16 20:49:17 -040078
Selim Cinek1685e632014-04-08 02:27:49 +020079 /**
80 * Is this notification expanded by the system. The expansion state can be overridden by the
81 * user expansion.
82 */
83 private boolean mIsSystemExpanded;
Jorim Jaggidce3c4c2014-04-29 23:12:24 +020084
85 /**
Selim Cinek83bc7832015-10-22 13:26:54 -070086 * Whether the notification is on the keyguard and the expansion is disabled.
Jorim Jaggidce3c4c2014-04-29 23:12:24 +020087 */
Selim Cinek83bc7832015-10-22 13:26:54 -070088 private boolean mOnKeyguard;
Jorim Jaggidce3c4c2014-04-29 23:12:24 +020089
Jorim Jaggibe565df2014-04-28 17:51:23 +020090 private NotificationContentView mPublicLayout;
91 private NotificationContentView mPrivateLayout;
Selim Cinek1685e632014-04-08 02:27:49 +020092 private int mMaxExpandHeight;
Selim Cinek8d490d42015-04-10 00:05:50 -070093 private int mHeadsUpHeight;
Selim Cinek863834b2014-05-20 04:20:25 +020094 private View mVetoButton;
Dan Sandler0d3e62f2014-07-14 17:13:50 -040095 private boolean mClearable;
Chris Wren78403d72014-07-28 10:23:24 +010096 private ExpansionLogger mLogger;
97 private String mLoggingKey;
Selim Cineka5e211b2014-08-11 17:35:48 +020098 private boolean mWasReset;
Selim Cinek8d490d42015-04-10 00:05:50 -070099 private NotificationGuts mGuts;
Selim Cinekda42d652015-12-04 15:51:16 -0800100 private NotificationData.Entry mEntry;
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200101 private StatusBarNotification mStatusBarNotification;
Selim Cinek1a521f32014-11-03 17:39:29 +0100102 private boolean mIsHeadsUp;
Selim Cinek98713a42015-09-21 15:47:20 +0200103 private boolean mLastChronometerRunning = true;
Selim Cinekeef84282015-10-30 16:28:00 -0700104 private NotificationHeaderView mNotificationHeader;
Selim Cinek9c7712d2015-12-08 19:19:48 -0800105 private NotificationViewWrapper mNotificationHeaderWrapper;
Selim Cinekb5605e52015-02-20 18:21:41 +0100106 private ViewStub mChildrenContainerStub;
107 private NotificationGroupManager mGroupManager;
Selim Cinekb5605e52015-02-20 18:21:41 +0100108 private boolean mChildrenExpanded;
Selim Cinek263398f2015-10-21 17:40:23 -0700109 private boolean mIsSummaryWithChildren;
Selim Cinekb5605e52015-02-20 18:21:41 +0100110 private NotificationChildrenContainer mChildrenContainer;
Selim Cinekab29aeb2015-02-20 18:18:32 +0100111 private ViewStub mGutsStub;
Selim Cinekb5605e52015-02-20 18:21:41 +0100112 private boolean mIsSystemChildExpanded;
Selim Cinek684a4422015-04-15 16:18:39 -0700113 private boolean mIsPinned;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700114 private FalsingManager mFalsingManager;
Selim Cinek31aada42015-12-18 17:51:15 -0800115 private HeadsUpManager mHeadsUpManager;
Selim Cinekea4bef72015-12-02 15:51:10 -0800116 private NotificationHeaderUtil mHeaderUtil = new NotificationHeaderUtil(this);
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200117
Jorim Jaggi5eb67c22015-08-19 19:50:49 -0700118 private boolean mJustClicked;
Selim Cinek5a175d92015-11-23 18:01:33 -0800119 private boolean mIconAnimationRunning;
Selim Cinek34d93b02015-10-22 12:30:38 -0700120 private boolean mShowNoBackground;
Selim Cinek388df6d2015-10-22 13:25:11 -0700121 private ExpandableNotificationRow mNotificationParent;
Selim Cinek570981d2015-12-01 11:37:01 -0800122 private OnExpandClickListener mOnExpandClickListener;
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800123 private OnClickListener mExpandClickListener = new OnClickListener() {
124 @Override
125 public void onClick(View v) {
Selim Cinek624c02db2015-12-14 21:00:02 -0800126 if (!mShowingPublic && mGroupManager.isSummaryOfGroup(mStatusBarNotification)) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800127 mGroupManager.toggleGroupExpansion(mStatusBarNotification);
Selim Cinek31aada42015-12-18 17:51:15 -0800128 mOnExpandClickListener.onExpandClicked(mEntry,
Selim Cinek570981d2015-12-01 11:37:01 -0800129 mGroupManager.isGroupExpanded(mStatusBarNotification));
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800130 } else {
Selim Cinek31aada42015-12-18 17:51:15 -0800131 boolean nowExpanded;
132 if (isPinned()) {
133 nowExpanded = !mExpandedWhenPinned;
134 mExpandedWhenPinned = nowExpanded;
135 } else {
136 nowExpanded = !isExpanded();
137 setUserExpanded(nowExpanded);
138 }
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800139 notifyHeightChanged(true);
Selim Cinek31aada42015-12-18 17:51:15 -0800140 mOnExpandClickListener.onExpandClicked(mEntry, nowExpanded);
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800141 }
142 }
143 };
Jorim Jaggi5eb67c22015-08-19 19:50:49 -0700144
Selim Cinek8d490d42015-04-10 00:05:50 -0700145 public NotificationContentView getPrivateLayout() {
146 return mPrivateLayout;
147 }
148
149 public NotificationContentView getPublicLayout() {
150 return mPublicLayout;
151 }
152
Selim Cinekcab4a602014-09-03 14:47:57 +0200153 public void setIconAnimationRunning(boolean running) {
154 setIconAnimationRunning(running, mPublicLayout);
155 setIconAnimationRunning(running, mPrivateLayout);
Selim Cinek5a175d92015-11-23 18:01:33 -0800156 setIconAnimationRunningForChild(running, mNotificationHeader);
157 if (mIsSummaryWithChildren) {
158 List<ExpandableNotificationRow> notificationChildren =
159 mChildrenContainer.getNotificationChildren();
160 for (int i = 0; i < notificationChildren.size(); i++) {
161 ExpandableNotificationRow child = notificationChildren.get(i);
162 child.setIconAnimationRunning(running);
163 }
164 }
165 mIconAnimationRunning = running;
Selim Cinekcab4a602014-09-03 14:47:57 +0200166 }
167
168 private void setIconAnimationRunning(boolean running, NotificationContentView layout) {
169 if (layout != null) {
170 View contractedChild = layout.getContractedChild();
171 View expandedChild = layout.getExpandedChild();
Selim Cinek8d490d42015-04-10 00:05:50 -0700172 View headsUpChild = layout.getHeadsUpChild();
Selim Cinekcab4a602014-09-03 14:47:57 +0200173 setIconAnimationRunningForChild(running, contractedChild);
174 setIconAnimationRunningForChild(running, expandedChild);
Selim Cinek8d490d42015-04-10 00:05:50 -0700175 setIconAnimationRunningForChild(running, headsUpChild);
Selim Cinekcab4a602014-09-03 14:47:57 +0200176 }
177 }
178
179 private void setIconAnimationRunningForChild(boolean running, View child) {
180 if (child != null) {
181 ImageView icon = (ImageView) child.findViewById(com.android.internal.R.id.icon);
182 setIconRunning(icon, running);
183 ImageView rightIcon = (ImageView) child.findViewById(
184 com.android.internal.R.id.right_icon);
185 setIconRunning(rightIcon, running);
186 }
187 }
188
189 private void setIconRunning(ImageView imageView, boolean running) {
190 if (imageView != null) {
191 Drawable drawable = imageView.getDrawable();
192 if (drawable instanceof AnimationDrawable) {
193 AnimationDrawable animationDrawable = (AnimationDrawable) drawable;
194 if (running) {
195 animationDrawable.start();
196 } else {
197 animationDrawable.stop();
198 }
199 } else if (drawable instanceof AnimatedVectorDrawable) {
200 AnimatedVectorDrawable animationDrawable = (AnimatedVectorDrawable) drawable;
201 if (running) {
202 animationDrawable.start();
203 } else {
204 animationDrawable.stop();
205 }
206 }
207 }
208 }
209
Selim Cinekda42d652015-12-04 15:51:16 -0800210 public void onNotificationUpdated(NotificationData.Entry entry) {
211 mEntry = entry;
212 mStatusBarNotification = entry.notification;
Adrian Roosb88b1a12015-12-09 18:51:05 -0800213 mPrivateLayout.onNotificationUpdated(entry);
214 mPublicLayout.onNotificationUpdated(entry);
Selim Cinek757d8792016-01-28 16:21:08 -0800215 mShowingPublicInitialized = false;
216 updateClearability();
Selim Cinek8fc93c92015-11-23 17:48:07 -0800217 if (mIsSummaryWithChildren) {
218 recreateNotificationHeader();
219 }
Selim Cinek5a175d92015-11-23 18:01:33 -0800220 if (mIconAnimationRunning) {
221 setIconAnimationRunning(true);
222 }
Selim Cinekea4bef72015-12-02 15:51:10 -0800223 if (mNotificationParent != null) {
224 mNotificationParent.updateChildrenHeaderAppearance();
225 }
Selim Cinek263398f2015-10-21 17:40:23 -0700226 onChildrenCountChanged();
Selim Cinek624c02db2015-12-14 21:00:02 -0800227 // The public layouts expand button is always visible
228 mPublicLayout.updateExpandButtons(true);
Selim Cinekda42d652015-12-04 15:51:16 -0800229 updateLimits();
230 }
231
232 private void updateLimits() {
233 boolean customView = getPrivateLayout().getContractedChild().getId()
234 != com.android.internal.R.id.status_bar_latest_event_content;
235 boolean beforeN = mEntry.targetSdk < Build.VERSION_CODES.N;
236 int minHeight = customView && beforeN && !mIsSummaryWithChildren ?
237 mNotificationMinHeightLegacy : mNotificationMinHeight;
Selim Cinek77019c72015-12-09 10:18:02 -0800238 boolean headsUpCustom = getPrivateLayout().getHeadsUpChild() != null &&
239 getPrivateLayout().getHeadsUpChild().getId()
240 != com.android.internal.R.id.status_bar_latest_event_content;
241 int headsUpheight = headsUpCustom && beforeN ? mMaxHeadsUpHeightLegacy
242 : mMaxHeadsUpHeight;
Selim Cinekda42d652015-12-04 15:51:16 -0800243 mMaxViewHeight = mNotificationMaxHeight;
Selim Cinek860b6da2015-12-16 19:02:19 -0800244 mPrivateLayout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight);
245 mPublicLayout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight);
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200246 }
247
248 public StatusBarNotification getStatusBarNotification() {
249 return mStatusBarNotification;
250 }
251
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700252 public boolean isHeadsUp() {
253 return mIsHeadsUp;
254 }
255
Selim Cinek1a521f32014-11-03 17:39:29 +0100256 public void setHeadsUp(boolean isHeadsUp) {
Selim Cinekc80fdb12015-04-13 15:09:08 -0700257 int intrinsicBefore = getIntrinsicHeight();
Selim Cinek1a521f32014-11-03 17:39:29 +0100258 mIsHeadsUp = isHeadsUp;
Selim Cinek8d490d42015-04-10 00:05:50 -0700259 mPrivateLayout.setHeadsUp(isHeadsUp);
Selim Cinekc80fdb12015-04-13 15:09:08 -0700260 if (intrinsicBefore != getIntrinsicHeight()) {
261 notifyHeightChanged(false /* needsAnimation */);
262 }
Selim Cinek1a521f32014-11-03 17:39:29 +0100263 }
264
Selim Cinekb5605e52015-02-20 18:21:41 +0100265 public void setGroupManager(NotificationGroupManager groupManager) {
266 mGroupManager = groupManager;
Selim Cinek83bc7832015-10-22 13:26:54 -0700267 mPrivateLayout.setGroupManager(groupManager);
Selim Cinekb5605e52015-02-20 18:21:41 +0100268 }
269
Adrian Roosb88b1a12015-12-09 18:51:05 -0800270 public void setRemoteInputController(RemoteInputController r) {
271 mPrivateLayout.setRemoteInputController(r);
272 }
273
Selim Cinekb5605e52015-02-20 18:21:41 +0100274 public void addChildNotification(ExpandableNotificationRow row) {
275 addChildNotification(row, -1);
276 }
277
278 /**
279 * Add a child notification to this view.
280 *
281 * @param row the row to add
282 * @param childIndex the index to add it at, if -1 it will be added at the end
283 */
284 public void addChildNotification(ExpandableNotificationRow row, int childIndex) {
285 if (mChildrenContainer == null) {
286 mChildrenContainerStub.inflate();
287 }
288 mChildrenContainer.addNotification(row, childIndex);
Selim Cinek263398f2015-10-21 17:40:23 -0700289 onChildrenCountChanged();
290 row.setIsChildInGroup(true, this);
Selim Cinekb5605e52015-02-20 18:21:41 +0100291 }
292
293 public void removeChildNotification(ExpandableNotificationRow row) {
294 if (mChildrenContainer != null) {
295 mChildrenContainer.removeNotification(row);
296 }
Selim Cinekea4bef72015-12-02 15:51:10 -0800297 mHeaderUtil.restoreNotificationHeader(row);
Selim Cinek263398f2015-10-21 17:40:23 -0700298 onChildrenCountChanged();
299 row.setIsChildInGroup(false, null);
300 }
301
302 public boolean isChildInGroup() {
Selim Cineka6c6bfb2015-10-29 16:27:08 -0700303 return mNotificationParent != null;
Selim Cinek263398f2015-10-21 17:40:23 -0700304 }
305
Selim Cinek388df6d2015-10-22 13:25:11 -0700306 public ExpandableNotificationRow getNotificationParent() {
307 return mNotificationParent;
308 }
309
Selim Cinek263398f2015-10-21 17:40:23 -0700310 /**
311 * @param isChildInGroup Is this notification now in a group
312 * @param parent the new parent notification
313 */
Selim Cineka6c6bfb2015-10-29 16:27:08 -0700314 public void setIsChildInGroup(boolean isChildInGroup, ExpandableNotificationRow parent) {;
315 boolean childInGroup = BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS && isChildInGroup;
316 mNotificationParent = childInGroup ? parent : null;
317 mPrivateLayout.setIsChildInGroup(childInGroup);
318 updateNoBackgroundState();
Selim Cinek34d93b02015-10-22 12:30:38 -0700319 }
320
321 @Override
Selim Cinek72109472016-01-15 16:33:22 -0800322 public boolean onTouchEvent(MotionEvent event) {
323 if (event.getActionMasked() != MotionEvent.ACTION_DOWN
324 || !isChildInGroup() || isGroupExpanded()) {
325 return super.onTouchEvent(event);
326 } else {
327 return false;
328 }
329 }
330
331 @Override
Selim Cinek34d93b02015-10-22 12:30:38 -0700332 protected boolean shouldHideBackground() {
333 return super.shouldHideBackground() || mShowNoBackground;
Selim Cinek263398f2015-10-21 17:40:23 -0700334 }
335
336 @Override
337 public boolean isSummaryWithChildren() {
338 return mIsSummaryWithChildren;
Selim Cinekb5605e52015-02-20 18:21:41 +0100339 }
340
341 @Override
342 public boolean areChildrenExpanded() {
343 return mChildrenExpanded;
344 }
345
346 public List<ExpandableNotificationRow> getNotificationChildren() {
347 return mChildrenContainer == null ? null : mChildrenContainer.getNotificationChildren();
348 }
349
Selim Cinekeef84282015-10-30 16:28:00 -0700350 public int getNumberOfNotificationChildren() {
351 if (mChildrenContainer == null) {
352 return 0;
353 }
354 return mChildrenContainer.getNotificationChildren().size();
355 }
356
Selim Cinekb5605e52015-02-20 18:21:41 +0100357 /**
358 * Apply the order given in the list to the children.
359 *
360 * @param childOrder the new list order
361 * @return whether the list order has changed
362 */
363 public boolean applyChildOrder(List<ExpandableNotificationRow> childOrder) {
364 return mChildrenContainer != null && mChildrenContainer.applyChildOrder(childOrder);
365 }
366
367 public void getChildrenStates(StackScrollState resultState) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700368 if (mIsSummaryWithChildren) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100369 StackViewState parentState = resultState.getViewStateForView(this);
370 mChildrenContainer.getState(resultState, parentState);
371 }
372 }
373
374 public void applyChildrenState(StackScrollState state) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700375 if (mIsSummaryWithChildren) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100376 mChildrenContainer.applyState(state);
377 }
378 }
379
380 public void prepareExpansionChanged(StackScrollState state) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700381 if (mIsSummaryWithChildren) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100382 mChildrenContainer.prepareExpansionChanged(state);
383 }
384 }
385
386 public void startChildAnimation(StackScrollState finalState,
Selim Cinek99695592016-01-12 17:51:35 -0800387 StackStateAnimator stateAnimator, long delay, long duration) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700388 if (mIsSummaryWithChildren) {
Selim Cinek99695592016-01-12 17:51:35 -0800389 mChildrenContainer.startAnimationToState(finalState, stateAnimator, delay,
Selim Cinekb5605e52015-02-20 18:21:41 +0100390 duration);
391 }
392 }
393
394 public ExpandableNotificationRow getViewAtPosition(float y) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700395 if (!mIsSummaryWithChildren || !mChildrenExpanded) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100396 return this;
397 } else {
398 ExpandableNotificationRow view = mChildrenContainer.getViewAtPosition(y);
399 return view == null ? this : view;
400 }
401 }
402
Selim Cinekab29aeb2015-02-20 18:18:32 +0100403 public NotificationGuts getGuts() {
404 return mGuts;
405 }
406
Selim Cinek684a4422015-04-15 16:18:39 -0700407 /**
408 * Set this notification to be pinned to the top if {@link #isHeadsUp()} is true. By doing this
409 * the notification will be rendered on top of the screen.
410 *
411 * @param pinned whether it is pinned
412 */
413 public void setPinned(boolean pinned) {
414 mIsPinned = pinned;
Selim Cinek31aada42015-12-18 17:51:15 -0800415 if (pinned) {
416 setIconAnimationRunning(true);
417 mExpandedWhenPinned = false;
418 } else if (mExpandedWhenPinned) {
419 setUserExpanded(true);
420 }
Selim Cinek98713a42015-09-21 15:47:20 +0200421 setChronometerRunning(mLastChronometerRunning);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700422 }
423
Selim Cinek684a4422015-04-15 16:18:39 -0700424 public boolean isPinned() {
425 return mIsPinned;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700426 }
427
Selim Cinek31aada42015-12-18 17:51:15 -0800428 /**
429 * @param atLeastMinHeight should the value returned be at least the minimum height.
430 * Used to avoid cyclic calls
431 * @return the height of the heads up notification when pinned
432 */
433 public int getPinnedHeadsUpHeight(boolean atLeastMinHeight) {
Selim Cinek77019c72015-12-09 10:18:02 -0800434 if (mIsSummaryWithChildren) {
435 return mChildrenContainer.getIntrinsicHeight();
436 }
Selim Cinek31aada42015-12-18 17:51:15 -0800437 if(mExpandedWhenPinned) {
438 return Math.max(getMaxExpandHeight(), mHeadsUpHeight);
439 } else if (atLeastMinHeight) {
440 return Math.max(getMinHeight(), mHeadsUpHeight);
441 } else {
442 return mHeadsUpHeight;
443 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700444 }
445
Jorim Jaggi5eb67c22015-08-19 19:50:49 -0700446 /**
447 * Mark whether this notification was just clicked, i.e. the user has just clicked this
448 * notification in this frame.
449 */
450 public void setJustClicked(boolean justClicked) {
451 mJustClicked = justClicked;
452 }
453
454 /**
455 * @return true if this notification has been clicked in this frame, false otherwise
456 */
457 public boolean wasJustClicked() {
458 return mJustClicked;
459 }
460
Selim Cinek98713a42015-09-21 15:47:20 +0200461 public void setChronometerRunning(boolean running) {
462 mLastChronometerRunning = running;
463 setChronometerRunning(running, mPrivateLayout);
464 setChronometerRunning(running, mPublicLayout);
465 if (mChildrenContainer != null) {
466 List<ExpandableNotificationRow> notificationChildren =
467 mChildrenContainer.getNotificationChildren();
468 for (int i = 0; i < notificationChildren.size(); i++) {
469 ExpandableNotificationRow child = notificationChildren.get(i);
470 child.setChronometerRunning(running);
471 }
472 }
473 }
474
475 private void setChronometerRunning(boolean running, NotificationContentView layout) {
476 if (layout != null) {
477 running = running || isPinned();
478 View contractedChild = layout.getContractedChild();
479 View expandedChild = layout.getExpandedChild();
480 View headsUpChild = layout.getHeadsUpChild();
481 setChronometerRunningForChild(running, contractedChild);
482 setChronometerRunningForChild(running, expandedChild);
483 setChronometerRunningForChild(running, headsUpChild);
484 }
485 }
486
487 private void setChronometerRunningForChild(boolean running, View child) {
488 if (child != null) {
489 View chronometer = child.findViewById(com.android.internal.R.id.chronometer);
490 if (chronometer instanceof Chronometer) {
491 ((Chronometer) chronometer).setStarted(running);
492 }
493 }
494 }
495
Selim Cinekea4bef72015-12-02 15:51:10 -0800496 public NotificationHeaderView getNotificationHeader() {
497 if (mNotificationHeader != null) {
498 return mNotificationHeader;
Selim Cinek8d6440d2015-10-22 13:00:05 -0700499 }
Selim Cinekea4bef72015-12-02 15:51:10 -0800500 return mPrivateLayout.getNotificationHeader();
Selim Cinek8d6440d2015-10-22 13:00:05 -0700501 }
502
Selim Cinek570981d2015-12-01 11:37:01 -0800503 public void setOnExpandClickListener(OnExpandClickListener onExpandClickListener) {
504 mOnExpandClickListener = onExpandClickListener;
505 }
506
Selim Cinek31aada42015-12-18 17:51:15 -0800507 public void setHeadsUpManager(HeadsUpManager headsUpManager) {
508 mHeadsUpManager = headsUpManager;
509 }
510
Selim Cinek01af3342016-02-09 19:25:31 -0800511 public void reInflateViews() {
512 initDimens();
513 if (mIsSummaryWithChildren) {
514 removeView(mNotificationHeader);
515 mNotificationHeader = null;
516 recreateNotificationHeader();
517 if (mChildrenContainer != null) {
518 mChildrenContainer.reInflateViews();
519 }
520 }
521 if (mGuts != null) {
522 View oldGuts = mGuts;
523 int index = indexOfChild(oldGuts);
524 removeView(oldGuts);
525 mGuts = (NotificationGuts) LayoutInflater.from(mContext).inflate(
526 R.layout.notification_guts, this, false);
527 mGuts.setVisibility(oldGuts.getVisibility());
528 addView(mGuts, index);
529 }
530 }
531
Chris Wren78403d72014-07-28 10:23:24 +0100532 public interface ExpansionLogger {
533 public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
534 }
Selim Cinek1685e632014-04-08 02:27:49 +0200535
Chris Wren51c75102013-07-16 20:49:17 -0400536 public ExpandableNotificationRow(Context context, AttributeSet attrs) {
537 super(context, attrs);
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700538 mFalsingManager = FalsingManager.getInstance(context);
Selim Cinek01af3342016-02-09 19:25:31 -0800539 initDimens();
540 }
541
542 private void initDimens() {
Selim Cinekda42d652015-12-04 15:51:16 -0800543 mNotificationMinHeightLegacy = getResources().getDimensionPixelSize(
544 R.dimen.notification_min_height_legacy);
545 mNotificationMinHeight = getResources().getDimensionPixelSize(
546 R.dimen.notification_min_height);
547 mNotificationMaxHeight = getResources().getDimensionPixelSize(
548 R.dimen.notification_max_height);
Selim Cinek77019c72015-12-09 10:18:02 -0800549 mMaxHeadsUpHeightLegacy = getResources().getDimensionPixelSize(
550 R.dimen.notification_max_heads_up_height_legacy);
551 mMaxHeadsUpHeight = getResources().getDimensionPixelSize(
552 R.dimen.notification_max_heads_up_height);
Chris Wren51c75102013-07-16 20:49:17 -0400553 }
554
Christoph Studera7fe6312014-06-27 19:32:44 +0200555 /**
556 * Resets this view so it can be re-used for an updated notification.
557 */
Christoph Studer22f2ee52014-07-29 22:57:21 +0200558 @Override
Christoph Studera7fe6312014-06-27 19:32:44 +0200559 public void reset() {
Christoph Studer22f2ee52014-07-29 22:57:21 +0200560 super.reset();
Chris Wren78403d72014-07-28 10:23:24 +0100561 final boolean wasExpanded = isExpanded();
Selim Cinek379ff8f2015-02-20 17:03:16 +0100562 mMaxViewHeight = 0;
Christoph Studera7fe6312014-06-27 19:32:44 +0200563 mExpandable = false;
564 mHasUserChangedExpansion = false;
565 mUserLocked = false;
566 mShowingPublic = false;
Jorim Jaggiae441282014-08-01 02:45:18 +0200567 mSensitive = false;
568 mShowingPublicInitialized = false;
Christoph Studera7fe6312014-06-27 19:32:44 +0200569 mIsSystemExpanded = false;
Selim Cinek83bc7832015-10-22 13:26:54 -0700570 mOnKeyguard = false;
Selim Cinek1a521f32014-11-03 17:39:29 +0100571 mPublicLayout.reset(mIsHeadsUp);
572 mPrivateLayout.reset(mIsHeadsUp);
Selim Cinek31094df2014-08-14 19:28:15 +0200573 resetHeight();
574 logExpansionEvent(false, wasExpanded);
575 }
576
577 public void resetHeight() {
Selim Cinek1a521f32014-11-03 17:39:29 +0100578 if (mIsHeadsUp) {
579 resetActualHeight();
580 }
Christoph Studera7fe6312014-06-27 19:32:44 +0200581 mMaxExpandHeight = 0;
Selim Cinek8d490d42015-04-10 00:05:50 -0700582 mHeadsUpHeight = 0;
Selim Cineka5e211b2014-08-11 17:35:48 +0200583 mWasReset = true;
Selim Cinek31094df2014-08-14 19:28:15 +0200584 onHeightReset();
Selim Cinek6e28a672014-09-05 14:43:28 +0200585 requestLayout();
Christoph Studera7fe6312014-06-27 19:32:44 +0200586 }
587
Jorim Jaggi251957d2014-04-09 04:24:09 +0200588 @Override
589 protected void onFinishInflate() {
590 super.onFinishInflate();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200591 mPublicLayout = (NotificationContentView) findViewById(R.id.expandedPublic);
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800592 mPublicLayout.setContainingNotification(this);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200593 mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded);
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800594 mPrivateLayout.setExpandClickListener(mExpandClickListener);
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800595 mPrivateLayout.setContainingNotification(this);
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800596 mPublicLayout.setExpandClickListener(mExpandClickListener);
Selim Cinekab29aeb2015-02-20 18:18:32 +0100597 mGutsStub = (ViewStub) findViewById(R.id.notification_guts_stub);
598 mGutsStub.setOnInflateListener(new ViewStub.OnInflateListener() {
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200599 @Override
600 public void onInflate(ViewStub stub, View inflated) {
601 mGuts = (NotificationGuts) inflated;
602 mGuts.setClipTopAmount(getClipTopAmount());
603 mGuts.setActualHeight(getActualHeight());
Selim Cinekab29aeb2015-02-20 18:18:32 +0100604 mGutsStub = null;
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200605 }
606 });
Selim Cinekb5605e52015-02-20 18:21:41 +0100607 mChildrenContainerStub = (ViewStub) findViewById(R.id.child_container_stub);
608 mChildrenContainerStub.setOnInflateListener(new ViewStub.OnInflateListener() {
609
610 @Override
611 public void onInflate(ViewStub stub, View inflated) {
612 mChildrenContainer = (NotificationChildrenContainer) inflated;
Selim Cinek388df6d2015-10-22 13:25:11 -0700613 mChildrenContainer.setNotificationParent(ExpandableNotificationRow.this);
Selim Cinekb5605e52015-02-20 18:21:41 +0100614 }
615 });
Selim Cinek863834b2014-05-20 04:20:25 +0200616 mVetoButton = findViewById(R.id.veto);
Jorim Jaggife40f7d2014-04-28 15:20:04 +0200617 }
618
Selim Cinekab29aeb2015-02-20 18:18:32 +0100619 public void inflateGuts() {
620 if (mGuts == null) {
621 mGutsStub.inflate();
622 }
623 }
624
Selim Cinekda42d652015-12-04 15:51:16 -0800625 private void updateChildrenVisibility() {
Selim Cinekd84a5932015-12-15 11:45:36 -0800626 mPrivateLayout.setVisibility(!mShowingPublic && !mIsSummaryWithChildren ? VISIBLE
627 : INVISIBLE);
Selim Cinekef5127e2015-12-21 16:55:58 -0800628 if (mChildrenContainer != null) {
629 mChildrenContainer.setVisibility(!mShowingPublic && mIsSummaryWithChildren ? VISIBLE
630 : INVISIBLE);
Selim Cinekb5605e52015-02-20 18:21:41 +0100631 }
Selim Cinekef5127e2015-12-21 16:55:58 -0800632 if (mNotificationHeader != null) {
633 mNotificationHeader.setVisibility(!mShowingPublic && mIsSummaryWithChildren ? VISIBLE
634 : INVISIBLE);
635 }
Selim Cinekda42d652015-12-04 15:51:16 -0800636 // The limits might have changed if the view suddenly became a group or vice versa
637 updateLimits();
Selim Cinekb5605e52015-02-20 18:21:41 +0100638 }
639
Jorim Jaggife40f7d2014-04-28 15:20:04 +0200640 @Override
Alan Viverettea54956a2015-01-07 16:05:02 -0800641 public boolean onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event) {
642 if (super.onRequestSendAccessibilityEventInternal(child, event)) {
Jorim Jaggife40f7d2014-04-28 15:20:04 +0200643 // Add a record for the entire layout since its content is somehow small.
644 // The event comes from a leaf view that is interacted with.
645 AccessibilityEvent record = AccessibilityEvent.obtain();
646 onInitializeAccessibilityEvent(record);
647 dispatchPopulateAccessibilityEvent(record);
648 event.appendRecord(record);
649 return true;
650 }
651 return false;
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200652 }
Chris Wren51c75102013-07-16 20:49:17 -0400653
John Spurlocke15452b2014-08-21 09:44:39 -0400654 @Override
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100655 public void setDark(boolean dark, boolean fade, long delay) {
656 super.setDark(dark, fade, delay);
John Spurlocke15452b2014-08-21 09:44:39 -0400657 final NotificationContentView showing = getShowingLayout();
658 if (showing != null) {
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100659 showing.setDark(dark, fade, delay);
John Spurlocke15452b2014-08-21 09:44:39 -0400660 }
Selim Cinek9c7712d2015-12-08 19:19:48 -0800661 if (mIsSummaryWithChildren) {
662 mChildrenContainer.setDark(dark, fade, delay);
663 mNotificationHeaderWrapper.setDark(dark, fade, delay);
664 }
John Spurlocke15452b2014-08-21 09:44:39 -0400665 }
666
Chris Wren51c75102013-07-16 20:49:17 -0400667 public boolean isExpandable() {
Selim Cinek388df6d2015-10-22 13:25:11 -0700668 if (mIsSummaryWithChildren && !mShowingPublic) {
669 return !mChildrenExpanded;
670 }
Chris Wren51c75102013-07-16 20:49:17 -0400671 return mExpandable;
672 }
673
674 public void setExpandable(boolean expandable) {
675 mExpandable = expandable;
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800676 mPrivateLayout.updateExpandButtons(isExpandable());
Chris Wren51c75102013-07-16 20:49:17 -0400677 }
678
Selim Cinek4ffd6362015-12-29 15:12:23 +0100679 @Override
680 public void setClipToActualHeight(boolean clipToActualHeight) {
Selim Cinek084c16b2016-01-22 17:48:22 -0800681 super.setClipToActualHeight(clipToActualHeight || isUserLocked());
682 getShowingLayout().setClipToActualHeight(clipToActualHeight || isUserLocked());
Selim Cinek4ffd6362015-12-29 15:12:23 +0100683 }
684
Selim Cinek1685e632014-04-08 02:27:49 +0200685 /**
686 * @return whether the user has changed the expansion state
687 */
688 public boolean hasUserChangedExpansion() {
689 return mHasUserChangedExpansion;
690 }
691
Chris Wren51c75102013-07-16 20:49:17 -0400692 public boolean isUserExpanded() {
693 return mUserExpanded;
694 }
695
Selim Cinek1685e632014-04-08 02:27:49 +0200696 /**
697 * Set this notification to be expanded by the user
698 *
699 * @param userExpanded whether the user wants this notification to be expanded
700 */
Chris Wren51c75102013-07-16 20:49:17 -0400701 public void setUserExpanded(boolean userExpanded) {
Selim Cinek388df6d2015-10-22 13:25:11 -0700702 setUserExpanded(userExpanded, false /* allowChildExpansion */);
703 }
704
705 /**
706 * Set this notification to be expanded by the user
707 *
708 * @param userExpanded whether the user wants this notification to be expanded
709 * @param allowChildExpansion whether a call to this method allows expanding children
710 */
711 public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700712 mFalsingManager.setNotificationExpanded();
Selim Cinek388df6d2015-10-22 13:25:11 -0700713 if (mIsSummaryWithChildren && !mShowingPublic && allowChildExpansion) {
714 mGroupManager.setGroupExpanded(mStatusBarNotification, userExpanded);
715 return;
716 }
Christoph Studera7fe6312014-06-27 19:32:44 +0200717 if (userExpanded && !mExpandable) return;
Chris Wren78403d72014-07-28 10:23:24 +0100718 final boolean wasExpanded = isExpanded();
Selim Cinek1685e632014-04-08 02:27:49 +0200719 mHasUserChangedExpansion = true;
Chris Wren51c75102013-07-16 20:49:17 -0400720 mUserExpanded = userExpanded;
Chris Wren78403d72014-07-28 10:23:24 +0100721 logExpansionEvent(true, wasExpanded);
Chris Wren51c75102013-07-16 20:49:17 -0400722 }
723
Selim Cinekccd14fb2014-08-12 18:53:24 +0200724 public void resetUserExpansion() {
725 mHasUserChangedExpansion = false;
726 mUserExpanded = false;
727 }
728
Chris Wren51c75102013-07-16 20:49:17 -0400729 public boolean isUserLocked() {
730 return mUserLocked;
731 }
732
733 public void setUserLocked(boolean userLocked) {
734 mUserLocked = userLocked;
735 }
736
Selim Cinek1685e632014-04-08 02:27:49 +0200737 /**
738 * @return has the system set this notification to be expanded
739 */
740 public boolean isSystemExpanded() {
741 return mIsSystemExpanded;
742 }
743
744 /**
745 * Set this notification to be expanded by the system.
746 *
747 * @param expand whether the system wants this notification to be expanded.
748 */
749 public void setSystemExpanded(boolean expand) {
Selim Cinek31094df2014-08-14 19:28:15 +0200750 if (expand != mIsSystemExpanded) {
751 final boolean wasExpanded = isExpanded();
752 mIsSystemExpanded = expand;
Selim Cinekb5605e52015-02-20 18:21:41 +0100753 notifyHeightChanged(false /* needsAnimation */);
Selim Cinek31094df2014-08-14 19:28:15 +0200754 logExpansionEvent(false, wasExpanded);
Selim Cinek83bc7832015-10-22 13:26:54 -0700755 if (mChildrenContainer != null) {
756 mChildrenContainer.updateGroupOverflow();
757 }
Selim Cinek31094df2014-08-14 19:28:15 +0200758 }
Jorim Jaggidce3c4c2014-04-29 23:12:24 +0200759 }
760
761 /**
Selim Cinek83bc7832015-10-22 13:26:54 -0700762 * @param onKeyguard whether to prevent notification expansion
Jorim Jaggidce3c4c2014-04-29 23:12:24 +0200763 */
Selim Cinek83bc7832015-10-22 13:26:54 -0700764 public void setOnKeyguard(boolean onKeyguard) {
765 if (onKeyguard != mOnKeyguard) {
Selim Cinek31094df2014-08-14 19:28:15 +0200766 final boolean wasExpanded = isExpanded();
Selim Cinek83bc7832015-10-22 13:26:54 -0700767 mOnKeyguard = onKeyguard;
Selim Cinek31094df2014-08-14 19:28:15 +0200768 logExpansionEvent(false, wasExpanded);
769 if (wasExpanded != isExpanded()) {
Selim Cinek2108fe02015-12-10 12:56:13 -0800770 if (mIsSummaryWithChildren) {
771 mChildrenContainer.updateGroupOverflow();
772 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100773 notifyHeightChanged(false /* needsAnimation */);
Selim Cinek31094df2014-08-14 19:28:15 +0200774 }
775 }
Selim Cinek1685e632014-04-08 02:27:49 +0200776 }
777
778 /**
Dan Sandler0d3e62f2014-07-14 17:13:50 -0400779 * @return Can the underlying notification be cleared?
780 */
781 public boolean isClearable() {
Selim Cineka37774f2014-11-11 19:16:18 +0100782 return mStatusBarNotification != null && mStatusBarNotification.isClearable();
Dan Sandler0d3e62f2014-07-14 17:13:50 -0400783 }
784
785 /**
Selim Cinek1685e632014-04-08 02:27:49 +0200786 * Apply an expansion state to the layout.
Selim Cinek1685e632014-04-08 02:27:49 +0200787 */
Jorim Jaggidce3c4c2014-04-29 23:12:24 +0200788 public void applyExpansionToLayout() {
789 boolean expand = isExpanded();
Chris Wren51c75102013-07-16 20:49:17 -0400790 if (expand && mExpandable) {
Selim Cinekeef84282015-10-30 16:28:00 -0700791 setActualHeight(mMaxExpandHeight);
Chris Wren51c75102013-07-16 20:49:17 -0400792 } else {
Selim Cinek860b6da2015-12-16 19:02:19 -0800793 setActualHeight(getMinHeight());
Chris Wren51c75102013-07-16 20:49:17 -0400794 }
Chris Wren51c75102013-07-16 20:49:17 -0400795 }
Dan Sandlera5e0f412014-01-23 15:11:54 -0500796
Jorim Jaggi9cbadd32014-05-01 20:18:31 +0200797 @Override
798 public int getIntrinsicHeight() {
Jorim Jaggibe565df2014-04-28 17:51:23 +0200799 if (isUserLocked()) {
800 return getActualHeight();
801 }
Selim Cinekd84a5932015-12-15 11:45:36 -0800802 if (mGuts != null && mGuts.areGutsExposed()) {
803 return mGuts.getHeight();
804 } else if ((isChildInGroup() && !isGroupExpanded())) {
805 return mPrivateLayout.getMinHeight();
806 } else if (mSensitive && mHideSensitiveForIntrinsicHeight) {
807 return getMinHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -0700808 } else if (mIsSummaryWithChildren && !mOnKeyguard) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800809 return mChildrenContainer.getIntrinsicHeight();
Selim Cinek60122be2015-04-15 18:16:50 -0700810 } else if (mIsHeadsUp) {
Selim Cinek31aada42015-12-18 17:51:15 -0800811 if (isPinned()) {
812 return getPinnedHeadsUpHeight(true /* atLeastMinHeight */);
813 } else if (isExpanded()) {
Selim Cinekd84a5932015-12-15 11:45:36 -0800814 return Math.max(getMaxExpandHeight(), mHeadsUpHeight);
Selim Cinek8d490d42015-04-10 00:05:50 -0700815 } else {
Selim Cinekd84a5932015-12-15 11:45:36 -0800816 return Math.max(getMinHeight(), mHeadsUpHeight);
Selim Cinek8d490d42015-04-10 00:05:50 -0700817 }
Selim Cinek31aada42015-12-18 17:51:15 -0800818 } else if (isExpanded()) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700819 return getMaxExpandHeight();
Selim Cinekd84a5932015-12-15 11:45:36 -0800820 } else {
821 return getMinHeight();
Selim Cinek1685e632014-04-08 02:27:49 +0200822 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100823 }
Selim Cinek1685e632014-04-08 02:27:49 +0200824
Selim Cinekeef84282015-10-30 16:28:00 -0700825 private boolean isGroupExpanded() {
826 return mGroupManager.isGroupExpanded(mStatusBarNotification);
Selim Cinekb5605e52015-02-20 18:21:41 +0100827 }
828
Selim Cinekeef84282015-10-30 16:28:00 -0700829 /**
830 * @return whether this view has a header on the top of the content
831 */
832 private boolean hasNotificationHeader() {
833 return mIsSummaryWithChildren;
Selim Cinek1685e632014-04-08 02:27:49 +0200834 }
835
Selim Cinek263398f2015-10-21 17:40:23 -0700836 private void onChildrenCountChanged() {
Selim Cinek8fc93c92015-11-23 17:48:07 -0800837 mIsSummaryWithChildren = BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS
Selim Cinek263398f2015-10-21 17:40:23 -0700838 && mGroupManager.hasGroupChildren(mStatusBarNotification);
Selim Cinek8fc93c92015-11-23 17:48:07 -0800839 if (mIsSummaryWithChildren) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800840 if (mChildrenContainer == null) {
841 mChildrenContainerStub.inflate();
842 }
Selim Cinek8fc93c92015-11-23 17:48:07 -0800843 if (mNotificationHeader == null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800844 recreateNotificationHeader();
845 }
Selim Cinek263398f2015-10-21 17:40:23 -0700846 }
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800847 mPrivateLayout.updateExpandButtons(isExpandable());
Selim Cinekea4bef72015-12-02 15:51:10 -0800848 updateChildrenHeaderAppearance();
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800849 updateHeaderChildCount();
Selim Cinekda42d652015-12-04 15:51:16 -0800850 updateChildrenVisibility();
Selim Cinek263398f2015-10-21 17:40:23 -0700851 }
852
Selim Cinek1685e632014-04-08 02:27:49 +0200853 /**
854 * Check whether the view state is currently expanded. This is given by the system in {@link
855 * #setSystemExpanded(boolean)} and can be overridden by user expansion or
856 * collapsing in {@link #setUserExpanded(boolean)}. Note that the visual appearance of this
857 * view can differ from this state, if layout params are modified from outside.
858 *
859 * @return whether the view state is currently expanded.
860 */
Selim Cinek83bc7832015-10-22 13:26:54 -0700861 public boolean isExpanded() {
862 return !mOnKeyguard
Selim Cinekb5605e52015-02-20 18:21:41 +0100863 && (!hasUserChangedExpansion() && (isSystemExpanded() || isSystemChildExpanded())
864 || isUserExpanded());
865 }
866
867 private boolean isSystemChildExpanded() {
868 return mIsSystemChildExpanded;
869 }
870
871 public void setSystemChildExpanded(boolean expanded) {
872 mIsSystemChildExpanded = expanded;
Selim Cinek1685e632014-04-08 02:27:49 +0200873 }
874
875 @Override
876 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
877 super.onLayout(changed, left, top, right, bottom);
Selim Cineka5e211b2014-08-11 17:35:48 +0200878 boolean updateExpandHeight = mMaxExpandHeight == 0 && !mWasReset;
Selim Cinek8d490d42015-04-10 00:05:50 -0700879 updateMaxHeights();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200880 if (updateExpandHeight) {
Jorim Jaggidce3c4c2014-04-29 23:12:24 +0200881 applyExpansionToLayout();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200882 }
Selim Cineka5e211b2014-08-11 17:35:48 +0200883 mWasReset = false;
Selim Cinek1685e632014-04-08 02:27:49 +0200884 }
885
Selim Cinek8d490d42015-04-10 00:05:50 -0700886 private void updateMaxHeights() {
Selim Cinekd2319fb2014-09-01 19:41:54 +0200887 int intrinsicBefore = getIntrinsicHeight();
Selim Cinek8d490d42015-04-10 00:05:50 -0700888 View expandedChild = mPrivateLayout.getExpandedChild();
889 if (expandedChild == null) {
890 expandedChild = mPrivateLayout.getContractedChild();
891 }
892 mMaxExpandHeight = expandedChild.getHeight();
893 View headsUpChild = mPrivateLayout.getHeadsUpChild();
Selim Cinek1f3f5442015-04-10 17:54:46 -0700894 if (headsUpChild == null) {
895 headsUpChild = mPrivateLayout.getContractedChild();
Selim Cinek8d490d42015-04-10 00:05:50 -0700896 }
Selim Cinek1f3f5442015-04-10 17:54:46 -0700897 mHeadsUpHeight = headsUpChild.getHeight();
Selim Cinekd2319fb2014-09-01 19:41:54 +0200898 if (intrinsicBefore != getIntrinsicHeight()) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100899 notifyHeightChanged(false /* needsAnimation */);
Selim Cinekd2319fb2014-09-01 19:41:54 +0200900 }
901 }
902
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800903 @Override
904 public void notifyHeightChanged(boolean needsAnimation) {
905 super.notifyHeightChanged(needsAnimation);
906 getShowingLayout().requestSelectLayout(needsAnimation || isUserLocked());
907 }
908
Jorim Jaggiae441282014-08-01 02:45:18 +0200909 public void setSensitive(boolean sensitive) {
910 mSensitive = sensitive;
911 }
912
913 public void setHideSensitiveForIntrinsicHeight(boolean hideSensitive) {
Selim Cinek60122be2015-04-15 18:16:50 -0700914 mHideSensitiveForIntrinsicHeight = hideSensitive;
Jorim Jaggiae441282014-08-01 02:45:18 +0200915 }
916
917 public void setHideSensitive(boolean hideSensitive, boolean animated, long delay,
918 long duration) {
919 boolean oldShowingPublic = mShowingPublic;
920 mShowingPublic = mSensitive && hideSensitive;
921 if (mShowingPublicInitialized && mShowingPublic == oldShowingPublic) {
922 return;
923 }
Dan Sandlera5e0f412014-01-23 15:11:54 -0500924
925 // bail out if no public version
Selim Cinek1685e632014-04-08 02:27:49 +0200926 if (mPublicLayout.getChildCount() == 0) return;
Dan Sandlera5e0f412014-01-23 15:11:54 -0500927
Jorim Jaggiae441282014-08-01 02:45:18 +0200928 if (!animated) {
929 mPublicLayout.animate().cancel();
930 mPrivateLayout.animate().cancel();
931 mPublicLayout.setAlpha(1f);
932 mPrivateLayout.setAlpha(1f);
933 mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE);
Selim Cinekd84a5932015-12-15 11:45:36 -0800934 updateChildrenVisibility();
Jorim Jaggiae441282014-08-01 02:45:18 +0200935 } else {
936 animateShowingPublic(delay, duration);
937 }
Dan Sandler0d3e62f2014-07-14 17:13:50 -0400938
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800939 mPrivateLayout.updateExpandButtons(isExpandable());
Selim Cinek757d8792016-01-28 16:21:08 -0800940 updateClearability();
Jorim Jaggiae441282014-08-01 02:45:18 +0200941 mShowingPublicInitialized = true;
942 }
943
944 private void animateShowingPublic(long delay, long duration) {
Selim Cinekd84a5932015-12-15 11:45:36 -0800945 View[] privateViews = mIsSummaryWithChildren ?
946 new View[] {mChildrenContainer, mNotificationHeader}
947 : new View[] {mPrivateLayout};
948 View[] publicViews = new View[] {mPublicLayout};
949 View[] hiddenChildren = mShowingPublic ? privateViews : publicViews;
950 View[] shownChildren = mShowingPublic ? publicViews : privateViews;
951 for (final View hiddenView : hiddenChildren) {
952 hiddenView.setVisibility(View.VISIBLE);
953 hiddenView.animate().cancel();
954 hiddenView.animate()
955 .alpha(0f)
956 .setStartDelay(delay)
957 .setDuration(duration)
958 .withEndAction(new Runnable() {
959 @Override
960 public void run() {
961 hiddenView.setVisibility(View.INVISIBLE);
962 }
963 });
964 }
965 for (View showView : shownChildren) {
966 showView.setVisibility(View.VISIBLE);
967 showView.setAlpha(0f);
968 showView.animate().cancel();
969 showView.animate()
970 .alpha(1f)
971 .setStartDelay(delay)
972 .setDuration(duration);
973 }
Dan Sandler0d3e62f2014-07-14 17:13:50 -0400974 }
975
Selim Cinek3776fe02016-02-04 13:32:43 -0800976 public boolean mustStayOnScreen() {
977 return mIsHeadsUp;
978 }
979
Selim Cinek757d8792016-01-28 16:21:08 -0800980 private void updateClearability() {
Dan Sandler0d3e62f2014-07-14 17:13:50 -0400981 // public versions cannot be dismissed
982 mVetoButton.setVisibility(isClearable() && !mShowingPublic ? View.VISIBLE : View.GONE);
Dan Sandlera5e0f412014-01-23 15:11:54 -0500983 }
Jorim Jaggi251957d2014-04-09 04:24:09 +0200984
Selim Cinekb5605e52015-02-20 18:21:41 +0100985 public void setChildrenExpanded(boolean expanded, boolean animate) {
986 mChildrenExpanded = expanded;
Selim Cinek7b836392015-12-04 20:02:59 -0800987 if (mNotificationHeader != null) {
988 mNotificationHeader.setExpanded(expanded);
989 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700990 if (mChildrenContainer != null) {
991 mChildrenContainer.setChildrenExpanded(expanded);
992 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100993 }
994
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800995 public void updateHeaderChildCount() {
996 if (mIsSummaryWithChildren) {
997 mNotificationHeader.setChildCount(
998 mChildrenContainer.getNotificationChildren().size());
Selim Cinekb5605e52015-02-20 18:21:41 +0100999 }
Selim Cinekb5605e52015-02-20 18:21:41 +01001000 }
1001
1002 public static void applyTint(View v, int color) {
1003 int alpha;
1004 if (color != 0) {
1005 alpha = COLORED_DIVIDER_ALPHA;
1006 } else {
1007 color = 0xff000000;
1008 alpha = DEFAULT_DIVIDER_ALPHA;
1009 }
1010 if (v.getBackground() instanceof ColorDrawable) {
1011 ColorDrawable background = (ColorDrawable) v.getBackground();
1012 background.mutate();
1013 background.setColor(color);
1014 background.setAlpha(alpha);
1015 }
1016 }
1017
Selim Cinek1685e632014-04-08 02:27:49 +02001018 public int getMaxExpandHeight() {
Selim Cinekb5605e52015-02-20 18:21:41 +01001019 return mMaxExpandHeight;
Chris Wren51c75102013-07-16 20:49:17 -04001020 }
Jorim Jaggi584a7aa2014-04-10 23:26:13 +02001021
Jorim Jaggibe565df2014-04-28 17:51:23 +02001022 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001023 public boolean isContentExpandable() {
Selim Cinek2f0df8a2014-06-10 17:40:42 +02001024 NotificationContentView showingLayout = getShowingLayout();
1025 return showingLayout.isContentExpandable();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001026 }
1027
1028 @Override
Selim Cinek560e64d2015-06-09 19:58:11 -07001029 protected View getContentView() {
1030 return getShowingLayout();
1031 }
1032
1033 @Override
Jorim Jaggid552d9d2014-05-07 19:41:13 +02001034 public void setActualHeight(int height, boolean notifyListeners) {
Selim Cinekb5605e52015-02-20 18:21:41 +01001035 super.setActualHeight(height, notifyListeners);
Selim Cinekeef84282015-10-30 16:28:00 -07001036 int contentHeight = Math.max(getMinHeight(), height);
Selim Cinekb5605e52015-02-20 18:21:41 +01001037 mPrivateLayout.setContentHeight(contentHeight);
1038 mPublicLayout.setContentHeight(contentHeight);
Jorim Jaggib1cd3c12014-09-08 19:55:17 +02001039 if (mGuts != null) {
1040 mGuts.setActualHeight(height);
1041 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001042 invalidate();
Jorim Jaggibe565df2014-04-28 17:51:23 +02001043 }
1044
1045 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01001046 public int getMaxContentHeight() {
Selim Cinek83bc7832015-10-22 13:26:54 -07001047 if (mIsSummaryWithChildren && !mShowingPublic) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001048 return mChildrenContainer.getMaxContentHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -07001049 }
Selim Cinek2f0df8a2014-06-10 17:40:42 +02001050 NotificationContentView showingLayout = getShowingLayout();
1051 return showingLayout.getMaxHeight();
Jorim Jaggibe565df2014-04-28 17:51:23 +02001052 }
1053
1054 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001055 public int getMinHeight() {
Selim Cinek31aada42015-12-18 17:51:15 -08001056 if (mIsHeadsUp && mHeadsUpManager.isTrackingHeadsUp()) {
1057 return getPinnedHeadsUpHeight(false /* atLeastMinHeight */);
1058 } else if (mIsSummaryWithChildren && !isGroupExpanded() && !mShowingPublic) {
Selim Cinekb55386d2015-12-16 17:26:49 -08001059 return mChildrenContainer.getMinHeight();
Selim Cinek31aada42015-12-18 17:51:15 -08001060 } else if (mIsHeadsUp) {
1061 return mHeadsUpHeight;
Selim Cinekb55386d2015-12-16 17:26:49 -08001062 }
Selim Cinek816c8e42015-11-19 12:00:45 -08001063 NotificationContentView showingLayout = getShowingLayout();
1064 return showingLayout.getMinHeight();
1065 }
1066
1067 @Override
1068 public int getMinExpandHeight() {
Selim Cinek83bc7832015-10-22 13:26:54 -07001069 if (mIsSummaryWithChildren && !mOnKeyguard) {
Selim Cinekb55386d2015-12-16 17:26:49 -08001070 return mChildrenContainer.getMinExpandHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -07001071 }
Selim Cinek816c8e42015-11-19 12:00:45 -08001072 return getMinHeight();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001073 }
1074
1075 @Override
Jorim Jaggibe565df2014-04-28 17:51:23 +02001076 public void setClipTopAmount(int clipTopAmount) {
1077 super.setClipTopAmount(clipTopAmount);
1078 mPrivateLayout.setClipTopAmount(clipTopAmount);
Selim Cinek2f0df8a2014-06-10 17:40:42 +02001079 mPublicLayout.setClipTopAmount(clipTopAmount);
Jorim Jaggib1cd3c12014-09-08 19:55:17 +02001080 if (mGuts != null) {
1081 mGuts.setClipTopAmount(clipTopAmount);
1082 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001083 }
1084
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001085 private void recreateNotificationHeader() {
1086 final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(),
1087 getStatusBarNotification().getNotification());
1088 final RemoteViews header = builder.makeNotificationHeader();
1089 if (mNotificationHeader == null) {
1090 mNotificationHeader = (NotificationHeaderView) header.apply(getContext(), this);
1091 final View expandButton = mNotificationHeader.findViewById(
1092 com.android.internal.R.id.expand_button);
1093 expandButton.setVisibility(VISIBLE);
1094 mNotificationHeader.setOnClickListener(mExpandClickListener);
Selim Cinek9c7712d2015-12-08 19:19:48 -08001095 mNotificationHeaderWrapper = NotificationViewWrapper.wrap(getContext(),
1096 mNotificationHeader);
Selim Cinekb5a83612015-12-11 14:14:39 -08001097 addView(mNotificationHeader, indexOfChild(mChildrenContainer) + 1);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001098 } else {
1099 header.reapply(getContext(), mNotificationHeader);
Selim Cinek4ffd6362015-12-29 15:12:23 +01001100 mNotificationHeaderWrapper.notifyContentUpdated(mEntry.notification);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001101 }
Selim Cinek7b836392015-12-04 20:02:59 -08001102 updateHeaderExpandButton();
Selim Cinekea4bef72015-12-02 15:51:10 -08001103 updateChildrenHeaderAppearance();
Selim Cinek8fc93c92015-11-23 17:48:07 -08001104 updateHeaderChildCount();
Selim Cinek343e6e22014-04-11 21:23:30 +02001105 }
Selim Cinek7d447722014-06-10 15:51:59 +02001106
Selim Cinek7b836392015-12-04 20:02:59 -08001107 private void updateHeaderExpandButton() {
1108 if (mIsSummaryWithChildren) {
1109 mNotificationHeader.setIsGroupHeader(true /* isGroupHeader*/);
1110 }
1111 }
1112
Selim Cinekea4bef72015-12-02 15:51:10 -08001113 public void updateChildrenHeaderAppearance() {
1114 if (mIsSummaryWithChildren) {
1115 mHeaderUtil.updateChildrenHeaderAppearance();
1116 }
1117 }
1118
Selim Cinek31094df2014-08-14 19:28:15 +02001119 public boolean isMaxExpandHeightInitialized() {
1120 return mMaxExpandHeight != 0;
Selim Cinek7d447722014-06-10 15:51:59 +02001121 }
Selim Cinek2f0df8a2014-06-10 17:40:42 +02001122
1123 private NotificationContentView getShowingLayout() {
1124 return mShowingPublic ? mPublicLayout : mPrivateLayout;
1125 }
Chris Wren78403d72014-07-28 10:23:24 +01001126
Jorim Jaggi59ec3042015-06-05 15:18:43 -07001127 @Override
1128 public void setShowingLegacyBackground(boolean showing) {
1129 super.setShowingLegacyBackground(showing);
1130 mPrivateLayout.setShowingLegacyBackground(showing);
1131 mPublicLayout.setShowingLegacyBackground(showing);
1132 }
1133
Selim Cineka6c6bfb2015-10-29 16:27:08 -07001134 @Override
1135 protected void updateBackgroundTint() {
1136 super.updateBackgroundTint();
1137 updateNoBackgroundState();
1138 if (mIsSummaryWithChildren) {
1139 List<ExpandableNotificationRow> notificationChildren =
1140 mChildrenContainer.getNotificationChildren();
1141 for (int i = 0; i < notificationChildren.size(); i++) {
1142 ExpandableNotificationRow child = notificationChildren.get(i);
1143 child.updateNoBackgroundState();
1144 }
1145 }
1146 }
1147
1148 private void updateNoBackgroundState() {
1149 mShowNoBackground = isChildInGroup() && hasSameBgColor(mNotificationParent);
1150 updateBackground();
1151 }
1152
Chris Wren78403d72014-07-28 10:23:24 +01001153 public void setExpansionLogger(ExpansionLogger logger, String key) {
1154 mLogger = logger;
1155 mLoggingKey = key;
1156 }
1157
Selim Cinek6183d122016-01-14 18:48:41 -08001158 @Override
Selim Cinek61633a82016-01-25 15:54:10 -08001159 public boolean needsIncreasedPadding() {
1160 return mIsSummaryWithChildren && isGroupExpanded();
1161 }
1162
1163 @Override
Selim Cinek6183d122016-01-14 18:48:41 -08001164 protected boolean disallowSingleClick(MotionEvent event) {
1165 float x = event.getX();
1166 float y = event.getY();
1167 NotificationHeaderView header = getNotificationHeader();
1168 if (header != null) {
1169 return header.isInTouchRect(x, y);
1170 }
1171 return super.disallowSingleClick(event);
1172 }
1173
Chris Wren78403d72014-07-28 10:23:24 +01001174 private void logExpansionEvent(boolean userAction, boolean wasExpanded) {
1175 final boolean nowExpanded = isExpanded();
1176 if (wasExpanded != nowExpanded && mLogger != null) {
1177 mLogger.logNotificationExpansion(mLoggingKey, userAction, nowExpanded) ;
1178 }
1179 }
Selim Cinek570981d2015-12-01 11:37:01 -08001180
1181 public interface OnExpandClickListener {
Selim Cinek31aada42015-12-18 17:51:15 -08001182 void onExpandClicked(NotificationData.Entry clickedEntry, boolean nowExpanded);
Selim Cinek570981d2015-12-01 11:37:01 -08001183 }
Chris Wren51c75102013-07-16 20:49:17 -04001184}