blob: f80630b6317b6fcc03bb01ecd06159f91f173d0d [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 Cinekeaa29ca2015-11-23 13:51:13 -080028import android.view.NotificationHeaderView;
Dan Sandlera5e0f412014-01-23 15:11:54 -050029import android.view.View;
Jorim Jaggib1cd3c12014-09-08 19:55:17 +020030import android.view.ViewStub;
Jorim Jaggife40f7d2014-04-28 15:20:04 +020031import android.view.accessibility.AccessibilityEvent;
Selim Cinek98713a42015-09-21 15:47:20 +020032import android.widget.Chronometer;
Selim Cinekcab4a602014-09-03 14:47:57 +020033import android.widget.ImageView;
Selim Cinekeaa29ca2015-11-23 13:51:13 -080034import android.widget.RemoteViews;
Selim Cinekb5605e52015-02-20 18:21:41 +010035
Dan Sandlera5e0f412014-01-23 15:11:54 -050036import com.android.systemui.R;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070037import com.android.systemui.classifier.FalsingManager;
Selim Cinek0ffbda62016-01-01 20:29:12 +010038import com.android.systemui.statusbar.notification.NotificationViewWrapper;
Selim Cinekb5605e52015-02-20 18:21:41 +010039import com.android.systemui.statusbar.phone.NotificationGroupManager;
Selim Cinek31aada42015-12-18 17:51:15 -080040import com.android.systemui.statusbar.policy.HeadsUpManager;
Selim Cinekb5605e52015-02-20 18:21:41 +010041import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
42import com.android.systemui.statusbar.stack.StackScrollState;
43import com.android.systemui.statusbar.stack.StackStateAnimator;
44import com.android.systemui.statusbar.stack.StackViewState;
45
46import java.util.List;
Dan Sandlera5e0f412014-01-23 15:11:54 -050047
Jorim Jaggi4222d9a2014-04-23 16:13:15 +020048public class ExpandableNotificationRow extends ActivatableNotificationView {
Selim Cinekb5605e52015-02-20 18:21:41 +010049
50 private static final int DEFAULT_DIVIDER_ALPHA = 0x29;
51 private static final int COLORED_DIVIDER_ALPHA = 0x7B;
Selim Cinekda42d652015-12-04 15:51:16 -080052 private final int mNotificationMinHeightLegacy;
Selim Cinek77019c72015-12-09 10:18:02 -080053 private final int mMaxHeadsUpHeightLegacy;
54 private final int mMaxHeadsUpHeight;
Selim Cinekda42d652015-12-04 15:51:16 -080055 private final int mNotificationMinHeight;
56 private final int mNotificationMaxHeight;
Chris Wren51c75102013-07-16 20:49:17 -040057
Selim Cinek1685e632014-04-08 02:27:49 +020058 /** Does this row contain layouts that can adapt to row expansion */
Chris Wren51c75102013-07-16 20:49:17 -040059 private boolean mExpandable;
Selim Cinek1685e632014-04-08 02:27:49 +020060 /** Has the user actively changed the expansion state of this row */
61 private boolean mHasUserChangedExpansion;
62 /** If {@link #mHasUserChangedExpansion}, has the user expanded this row */
Chris Wren51c75102013-07-16 20:49:17 -040063 private boolean mUserExpanded;
Selim Cinek31aada42015-12-18 17:51:15 -080064
65 /**
66 * Has this notification been expanded while it was pinned
67 */
68 private boolean mExpandedWhenPinned;
Selim Cinek1685e632014-04-08 02:27:49 +020069 /** Is the user touching this row */
Chris Wren51c75102013-07-16 20:49:17 -040070 private boolean mUserLocked;
Selim Cinek1685e632014-04-08 02:27:49 +020071 /** Are we showing the "public" version */
Dan Sandlera5e0f412014-01-23 15:11:54 -050072 private boolean mShowingPublic;
Jorim Jaggiae441282014-08-01 02:45:18 +020073 private boolean mSensitive;
74 private boolean mShowingPublicInitialized;
Selim Cinek60122be2015-04-15 18:16:50 -070075 private boolean mHideSensitiveForIntrinsicHeight;
Chris Wren51c75102013-07-16 20:49:17 -040076
Selim Cinek1685e632014-04-08 02:27:49 +020077 /**
78 * Is this notification expanded by the system. The expansion state can be overridden by the
79 * user expansion.
80 */
81 private boolean mIsSystemExpanded;
Jorim Jaggidce3c4c2014-04-29 23:12:24 +020082
83 /**
Selim Cinek83bc7832015-10-22 13:26:54 -070084 * Whether the notification is on the keyguard and the expansion is disabled.
Jorim Jaggidce3c4c2014-04-29 23:12:24 +020085 */
Selim Cinek83bc7832015-10-22 13:26:54 -070086 private boolean mOnKeyguard;
Jorim Jaggidce3c4c2014-04-29 23:12:24 +020087
Jorim Jaggibe565df2014-04-28 17:51:23 +020088 private NotificationContentView mPublicLayout;
89 private NotificationContentView mPrivateLayout;
Selim Cinek1685e632014-04-08 02:27:49 +020090 private int mMaxExpandHeight;
Selim Cinek8d490d42015-04-10 00:05:50 -070091 private int mHeadsUpHeight;
Selim Cinek863834b2014-05-20 04:20:25 +020092 private View mVetoButton;
Dan Sandler0d3e62f2014-07-14 17:13:50 -040093 private boolean mClearable;
Chris Wren78403d72014-07-28 10:23:24 +010094 private ExpansionLogger mLogger;
95 private String mLoggingKey;
Selim Cineka5e211b2014-08-11 17:35:48 +020096 private boolean mWasReset;
Selim Cinek8d490d42015-04-10 00:05:50 -070097 private NotificationGuts mGuts;
Selim Cinekda42d652015-12-04 15:51:16 -080098 private NotificationData.Entry mEntry;
Jorim Jaggib1cd3c12014-09-08 19:55:17 +020099 private StatusBarNotification mStatusBarNotification;
Selim Cinek1a521f32014-11-03 17:39:29 +0100100 private boolean mIsHeadsUp;
Selim Cinek98713a42015-09-21 15:47:20 +0200101 private boolean mLastChronometerRunning = true;
Selim Cinekeef84282015-10-30 16:28:00 -0700102 private NotificationHeaderView mNotificationHeader;
Selim Cinek9c7712d2015-12-08 19:19:48 -0800103 private NotificationViewWrapper mNotificationHeaderWrapper;
Selim Cinekb5605e52015-02-20 18:21:41 +0100104 private ViewStub mChildrenContainerStub;
105 private NotificationGroupManager mGroupManager;
Selim Cinekb5605e52015-02-20 18:21:41 +0100106 private boolean mChildrenExpanded;
Selim Cinek263398f2015-10-21 17:40:23 -0700107 private boolean mIsSummaryWithChildren;
Selim Cinekb5605e52015-02-20 18:21:41 +0100108 private NotificationChildrenContainer mChildrenContainer;
Selim Cinekab29aeb2015-02-20 18:18:32 +0100109 private ViewStub mGutsStub;
Selim Cinekb5605e52015-02-20 18:21:41 +0100110 private boolean mIsSystemChildExpanded;
Selim Cinek684a4422015-04-15 16:18:39 -0700111 private boolean mIsPinned;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700112 private FalsingManager mFalsingManager;
Selim Cinek31aada42015-12-18 17:51:15 -0800113 private HeadsUpManager mHeadsUpManager;
Selim Cinekea4bef72015-12-02 15:51:10 -0800114 private NotificationHeaderUtil mHeaderUtil = new NotificationHeaderUtil(this);
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200115
Jorim Jaggi5eb67c22015-08-19 19:50:49 -0700116 private boolean mJustClicked;
Selim Cinek5a175d92015-11-23 18:01:33 -0800117 private boolean mIconAnimationRunning;
Selim Cinek34d93b02015-10-22 12:30:38 -0700118 private boolean mShowNoBackground;
Selim Cinek388df6d2015-10-22 13:25:11 -0700119 private ExpandableNotificationRow mNotificationParent;
Selim Cinek570981d2015-12-01 11:37:01 -0800120 private OnExpandClickListener mOnExpandClickListener;
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800121 private OnClickListener mExpandClickListener = new OnClickListener() {
122 @Override
123 public void onClick(View v) {
Selim Cinek624c02db2015-12-14 21:00:02 -0800124 if (!mShowingPublic && mGroupManager.isSummaryOfGroup(mStatusBarNotification)) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800125 mGroupManager.toggleGroupExpansion(mStatusBarNotification);
Selim Cinek31aada42015-12-18 17:51:15 -0800126 mOnExpandClickListener.onExpandClicked(mEntry,
Selim Cinek570981d2015-12-01 11:37:01 -0800127 mGroupManager.isGroupExpanded(mStatusBarNotification));
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800128 } else {
Selim Cinek31aada42015-12-18 17:51:15 -0800129 boolean nowExpanded;
130 if (isPinned()) {
131 nowExpanded = !mExpandedWhenPinned;
132 mExpandedWhenPinned = nowExpanded;
133 } else {
134 nowExpanded = !isExpanded();
135 setUserExpanded(nowExpanded);
136 }
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800137 notifyHeightChanged(true);
Selim Cinek31aada42015-12-18 17:51:15 -0800138 mOnExpandClickListener.onExpandClicked(mEntry, nowExpanded);
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800139 }
140 }
141 };
Jorim Jaggi5eb67c22015-08-19 19:50:49 -0700142
Selim Cinek8d490d42015-04-10 00:05:50 -0700143 public NotificationContentView getPrivateLayout() {
144 return mPrivateLayout;
145 }
146
147 public NotificationContentView getPublicLayout() {
148 return mPublicLayout;
149 }
150
Selim Cinekcab4a602014-09-03 14:47:57 +0200151 public void setIconAnimationRunning(boolean running) {
152 setIconAnimationRunning(running, mPublicLayout);
153 setIconAnimationRunning(running, mPrivateLayout);
Selim Cinek5a175d92015-11-23 18:01:33 -0800154 setIconAnimationRunningForChild(running, mNotificationHeader);
155 if (mIsSummaryWithChildren) {
156 List<ExpandableNotificationRow> notificationChildren =
157 mChildrenContainer.getNotificationChildren();
158 for (int i = 0; i < notificationChildren.size(); i++) {
159 ExpandableNotificationRow child = notificationChildren.get(i);
160 child.setIconAnimationRunning(running);
161 }
162 }
163 mIconAnimationRunning = running;
Selim Cinekcab4a602014-09-03 14:47:57 +0200164 }
165
166 private void setIconAnimationRunning(boolean running, NotificationContentView layout) {
167 if (layout != null) {
168 View contractedChild = layout.getContractedChild();
169 View expandedChild = layout.getExpandedChild();
Selim Cinek8d490d42015-04-10 00:05:50 -0700170 View headsUpChild = layout.getHeadsUpChild();
Selim Cinekcab4a602014-09-03 14:47:57 +0200171 setIconAnimationRunningForChild(running, contractedChild);
172 setIconAnimationRunningForChild(running, expandedChild);
Selim Cinek8d490d42015-04-10 00:05:50 -0700173 setIconAnimationRunningForChild(running, headsUpChild);
Selim Cinekcab4a602014-09-03 14:47:57 +0200174 }
175 }
176
177 private void setIconAnimationRunningForChild(boolean running, View child) {
178 if (child != null) {
179 ImageView icon = (ImageView) child.findViewById(com.android.internal.R.id.icon);
180 setIconRunning(icon, running);
181 ImageView rightIcon = (ImageView) child.findViewById(
182 com.android.internal.R.id.right_icon);
183 setIconRunning(rightIcon, running);
184 }
185 }
186
187 private void setIconRunning(ImageView imageView, boolean running) {
188 if (imageView != null) {
189 Drawable drawable = imageView.getDrawable();
190 if (drawable instanceof AnimationDrawable) {
191 AnimationDrawable animationDrawable = (AnimationDrawable) drawable;
192 if (running) {
193 animationDrawable.start();
194 } else {
195 animationDrawable.stop();
196 }
197 } else if (drawable instanceof AnimatedVectorDrawable) {
198 AnimatedVectorDrawable animationDrawable = (AnimatedVectorDrawable) drawable;
199 if (running) {
200 animationDrawable.start();
201 } else {
202 animationDrawable.stop();
203 }
204 }
205 }
206 }
207
Selim Cinekda42d652015-12-04 15:51:16 -0800208 public void onNotificationUpdated(NotificationData.Entry entry) {
209 mEntry = entry;
210 mStatusBarNotification = entry.notification;
Adrian Roosb88b1a12015-12-09 18:51:05 -0800211 mPrivateLayout.onNotificationUpdated(entry);
212 mPublicLayout.onNotificationUpdated(entry);
Selim Cineka37774f2014-11-11 19:16:18 +0100213 updateVetoButton();
Selim Cinek8fc93c92015-11-23 17:48:07 -0800214 if (mIsSummaryWithChildren) {
215 recreateNotificationHeader();
216 }
Selim Cinek5a175d92015-11-23 18:01:33 -0800217 if (mIconAnimationRunning) {
218 setIconAnimationRunning(true);
219 }
Selim Cinekea4bef72015-12-02 15:51:10 -0800220 if (mNotificationParent != null) {
221 mNotificationParent.updateChildrenHeaderAppearance();
222 }
Selim Cinek263398f2015-10-21 17:40:23 -0700223 onChildrenCountChanged();
Selim Cinek624c02db2015-12-14 21:00:02 -0800224 // The public layouts expand button is always visible
225 mPublicLayout.updateExpandButtons(true);
Selim Cinekda42d652015-12-04 15:51:16 -0800226 updateLimits();
227 }
228
229 private void updateLimits() {
230 boolean customView = getPrivateLayout().getContractedChild().getId()
231 != com.android.internal.R.id.status_bar_latest_event_content;
232 boolean beforeN = mEntry.targetSdk < Build.VERSION_CODES.N;
233 int minHeight = customView && beforeN && !mIsSummaryWithChildren ?
234 mNotificationMinHeightLegacy : mNotificationMinHeight;
Selim Cinek77019c72015-12-09 10:18:02 -0800235 boolean headsUpCustom = getPrivateLayout().getHeadsUpChild() != null &&
236 getPrivateLayout().getHeadsUpChild().getId()
237 != com.android.internal.R.id.status_bar_latest_event_content;
238 int headsUpheight = headsUpCustom && beforeN ? mMaxHeadsUpHeightLegacy
239 : mMaxHeadsUpHeight;
Selim Cinekda42d652015-12-04 15:51:16 -0800240 mMaxViewHeight = mNotificationMaxHeight;
Selim Cinek860b6da2015-12-16 19:02:19 -0800241 mPrivateLayout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight);
242 mPublicLayout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight);
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200243 }
244
245 public StatusBarNotification getStatusBarNotification() {
246 return mStatusBarNotification;
247 }
248
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700249 public boolean isHeadsUp() {
250 return mIsHeadsUp;
251 }
252
Selim Cinek1a521f32014-11-03 17:39:29 +0100253 public void setHeadsUp(boolean isHeadsUp) {
Selim Cinekc80fdb12015-04-13 15:09:08 -0700254 int intrinsicBefore = getIntrinsicHeight();
Selim Cinek1a521f32014-11-03 17:39:29 +0100255 mIsHeadsUp = isHeadsUp;
Selim Cinek8d490d42015-04-10 00:05:50 -0700256 mPrivateLayout.setHeadsUp(isHeadsUp);
Selim Cinekc80fdb12015-04-13 15:09:08 -0700257 if (intrinsicBefore != getIntrinsicHeight()) {
258 notifyHeightChanged(false /* needsAnimation */);
259 }
Selim Cinek1a521f32014-11-03 17:39:29 +0100260 }
261
Selim Cinekb5605e52015-02-20 18:21:41 +0100262 public void setGroupManager(NotificationGroupManager groupManager) {
263 mGroupManager = groupManager;
Selim Cinek83bc7832015-10-22 13:26:54 -0700264 mPrivateLayout.setGroupManager(groupManager);
Selim Cinekb5605e52015-02-20 18:21:41 +0100265 }
266
Adrian Roosb88b1a12015-12-09 18:51:05 -0800267 public void setRemoteInputController(RemoteInputController r) {
268 mPrivateLayout.setRemoteInputController(r);
269 }
270
Selim Cinekb5605e52015-02-20 18:21:41 +0100271 public void addChildNotification(ExpandableNotificationRow row) {
272 addChildNotification(row, -1);
273 }
274
275 /**
276 * Add a child notification to this view.
277 *
278 * @param row the row to add
279 * @param childIndex the index to add it at, if -1 it will be added at the end
280 */
281 public void addChildNotification(ExpandableNotificationRow row, int childIndex) {
282 if (mChildrenContainer == null) {
283 mChildrenContainerStub.inflate();
284 }
285 mChildrenContainer.addNotification(row, childIndex);
Selim Cinek263398f2015-10-21 17:40:23 -0700286 onChildrenCountChanged();
287 row.setIsChildInGroup(true, this);
Selim Cinekb5605e52015-02-20 18:21:41 +0100288 }
289
290 public void removeChildNotification(ExpandableNotificationRow row) {
291 if (mChildrenContainer != null) {
292 mChildrenContainer.removeNotification(row);
293 }
Selim Cinekea4bef72015-12-02 15:51:10 -0800294 mHeaderUtil.restoreNotificationHeader(row);
Selim Cinek263398f2015-10-21 17:40:23 -0700295 onChildrenCountChanged();
296 row.setIsChildInGroup(false, null);
297 }
298
299 public boolean isChildInGroup() {
Selim Cineka6c6bfb2015-10-29 16:27:08 -0700300 return mNotificationParent != null;
Selim Cinek263398f2015-10-21 17:40:23 -0700301 }
302
Selim Cinek388df6d2015-10-22 13:25:11 -0700303 public ExpandableNotificationRow getNotificationParent() {
304 return mNotificationParent;
305 }
306
Selim Cinek263398f2015-10-21 17:40:23 -0700307 /**
308 * @param isChildInGroup Is this notification now in a group
309 * @param parent the new parent notification
310 */
Selim Cineka6c6bfb2015-10-29 16:27:08 -0700311 public void setIsChildInGroup(boolean isChildInGroup, ExpandableNotificationRow parent) {;
312 boolean childInGroup = BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS && isChildInGroup;
313 mNotificationParent = childInGroup ? parent : null;
314 mPrivateLayout.setIsChildInGroup(childInGroup);
315 updateNoBackgroundState();
Selim Cinek34d93b02015-10-22 12:30:38 -0700316 }
317
318 @Override
319 protected boolean shouldHideBackground() {
320 return super.shouldHideBackground() || mShowNoBackground;
Selim Cinek263398f2015-10-21 17:40:23 -0700321 }
322
323 @Override
324 public boolean isSummaryWithChildren() {
325 return mIsSummaryWithChildren;
Selim Cinekb5605e52015-02-20 18:21:41 +0100326 }
327
328 @Override
329 public boolean areChildrenExpanded() {
330 return mChildrenExpanded;
331 }
332
333 public List<ExpandableNotificationRow> getNotificationChildren() {
334 return mChildrenContainer == null ? null : mChildrenContainer.getNotificationChildren();
335 }
336
Selim Cinekeef84282015-10-30 16:28:00 -0700337 public int getNumberOfNotificationChildren() {
338 if (mChildrenContainer == null) {
339 return 0;
340 }
341 return mChildrenContainer.getNotificationChildren().size();
342 }
343
Selim Cinekb5605e52015-02-20 18:21:41 +0100344 /**
345 * Apply the order given in the list to the children.
346 *
347 * @param childOrder the new list order
348 * @return whether the list order has changed
349 */
350 public boolean applyChildOrder(List<ExpandableNotificationRow> childOrder) {
351 return mChildrenContainer != null && mChildrenContainer.applyChildOrder(childOrder);
352 }
353
354 public void getChildrenStates(StackScrollState resultState) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700355 if (mIsSummaryWithChildren) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100356 StackViewState parentState = resultState.getViewStateForView(this);
357 mChildrenContainer.getState(resultState, parentState);
358 }
359 }
360
361 public void applyChildrenState(StackScrollState state) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700362 if (mIsSummaryWithChildren) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100363 mChildrenContainer.applyState(state);
364 }
365 }
366
367 public void prepareExpansionChanged(StackScrollState state) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700368 if (mIsSummaryWithChildren) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100369 mChildrenContainer.prepareExpansionChanged(state);
370 }
371 }
372
373 public void startChildAnimation(StackScrollState finalState,
Selim Cinek99695592016-01-12 17:51:35 -0800374 StackStateAnimator stateAnimator, long delay, long duration) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700375 if (mIsSummaryWithChildren) {
Selim Cinek99695592016-01-12 17:51:35 -0800376 mChildrenContainer.startAnimationToState(finalState, stateAnimator, delay,
Selim Cinekb5605e52015-02-20 18:21:41 +0100377 duration);
378 }
379 }
380
381 public ExpandableNotificationRow getViewAtPosition(float y) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700382 if (!mIsSummaryWithChildren || !mChildrenExpanded) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100383 return this;
384 } else {
385 ExpandableNotificationRow view = mChildrenContainer.getViewAtPosition(y);
386 return view == null ? this : view;
387 }
388 }
389
Selim Cinekab29aeb2015-02-20 18:18:32 +0100390 public NotificationGuts getGuts() {
391 return mGuts;
392 }
393
Selim Cinek684a4422015-04-15 16:18:39 -0700394 /**
395 * Set this notification to be pinned to the top if {@link #isHeadsUp()} is true. By doing this
396 * the notification will be rendered on top of the screen.
397 *
398 * @param pinned whether it is pinned
399 */
400 public void setPinned(boolean pinned) {
401 mIsPinned = pinned;
Selim Cinek31aada42015-12-18 17:51:15 -0800402 if (pinned) {
403 setIconAnimationRunning(true);
404 mExpandedWhenPinned = false;
405 } else if (mExpandedWhenPinned) {
406 setUserExpanded(true);
407 }
Selim Cinek98713a42015-09-21 15:47:20 +0200408 setChronometerRunning(mLastChronometerRunning);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700409 }
410
Selim Cinek684a4422015-04-15 16:18:39 -0700411 public boolean isPinned() {
412 return mIsPinned;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700413 }
414
Selim Cinek31aada42015-12-18 17:51:15 -0800415 /**
416 * @param atLeastMinHeight should the value returned be at least the minimum height.
417 * Used to avoid cyclic calls
418 * @return the height of the heads up notification when pinned
419 */
420 public int getPinnedHeadsUpHeight(boolean atLeastMinHeight) {
Selim Cinek77019c72015-12-09 10:18:02 -0800421 if (mIsSummaryWithChildren) {
422 return mChildrenContainer.getIntrinsicHeight();
423 }
Selim Cinek31aada42015-12-18 17:51:15 -0800424 if(mExpandedWhenPinned) {
425 return Math.max(getMaxExpandHeight(), mHeadsUpHeight);
426 } else if (atLeastMinHeight) {
427 return Math.max(getMinHeight(), mHeadsUpHeight);
428 } else {
429 return mHeadsUpHeight;
430 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700431 }
432
Jorim Jaggi5eb67c22015-08-19 19:50:49 -0700433 /**
434 * Mark whether this notification was just clicked, i.e. the user has just clicked this
435 * notification in this frame.
436 */
437 public void setJustClicked(boolean justClicked) {
438 mJustClicked = justClicked;
439 }
440
441 /**
442 * @return true if this notification has been clicked in this frame, false otherwise
443 */
444 public boolean wasJustClicked() {
445 return mJustClicked;
446 }
447
Selim Cinek98713a42015-09-21 15:47:20 +0200448 public void setChronometerRunning(boolean running) {
449 mLastChronometerRunning = running;
450 setChronometerRunning(running, mPrivateLayout);
451 setChronometerRunning(running, mPublicLayout);
452 if (mChildrenContainer != null) {
453 List<ExpandableNotificationRow> notificationChildren =
454 mChildrenContainer.getNotificationChildren();
455 for (int i = 0; i < notificationChildren.size(); i++) {
456 ExpandableNotificationRow child = notificationChildren.get(i);
457 child.setChronometerRunning(running);
458 }
459 }
460 }
461
462 private void setChronometerRunning(boolean running, NotificationContentView layout) {
463 if (layout != null) {
464 running = running || isPinned();
465 View contractedChild = layout.getContractedChild();
466 View expandedChild = layout.getExpandedChild();
467 View headsUpChild = layout.getHeadsUpChild();
468 setChronometerRunningForChild(running, contractedChild);
469 setChronometerRunningForChild(running, expandedChild);
470 setChronometerRunningForChild(running, headsUpChild);
471 }
472 }
473
474 private void setChronometerRunningForChild(boolean running, View child) {
475 if (child != null) {
476 View chronometer = child.findViewById(com.android.internal.R.id.chronometer);
477 if (chronometer instanceof Chronometer) {
478 ((Chronometer) chronometer).setStarted(running);
479 }
480 }
481 }
482
Selim Cinekea4bef72015-12-02 15:51:10 -0800483 public NotificationHeaderView getNotificationHeader() {
484 if (mNotificationHeader != null) {
485 return mNotificationHeader;
Selim Cinek8d6440d2015-10-22 13:00:05 -0700486 }
Selim Cinekea4bef72015-12-02 15:51:10 -0800487 return mPrivateLayout.getNotificationHeader();
Selim Cinek8d6440d2015-10-22 13:00:05 -0700488 }
489
Selim Cinek570981d2015-12-01 11:37:01 -0800490 public void setOnExpandClickListener(OnExpandClickListener onExpandClickListener) {
491 mOnExpandClickListener = onExpandClickListener;
492 }
493
Selim Cinek31aada42015-12-18 17:51:15 -0800494 public void setHeadsUpManager(HeadsUpManager headsUpManager) {
495 mHeadsUpManager = headsUpManager;
496 }
497
Chris Wren78403d72014-07-28 10:23:24 +0100498 public interface ExpansionLogger {
499 public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
500 }
Selim Cinek1685e632014-04-08 02:27:49 +0200501
Chris Wren51c75102013-07-16 20:49:17 -0400502 public ExpandableNotificationRow(Context context, AttributeSet attrs) {
503 super(context, attrs);
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700504 mFalsingManager = FalsingManager.getInstance(context);
Selim Cinekda42d652015-12-04 15:51:16 -0800505 mNotificationMinHeightLegacy = getResources().getDimensionPixelSize(
506 R.dimen.notification_min_height_legacy);
507 mNotificationMinHeight = getResources().getDimensionPixelSize(
508 R.dimen.notification_min_height);
509 mNotificationMaxHeight = getResources().getDimensionPixelSize(
510 R.dimen.notification_max_height);
Selim Cinek77019c72015-12-09 10:18:02 -0800511 mMaxHeadsUpHeightLegacy = getResources().getDimensionPixelSize(
512 R.dimen.notification_max_heads_up_height_legacy);
513 mMaxHeadsUpHeight = getResources().getDimensionPixelSize(
514 R.dimen.notification_max_heads_up_height);
Chris Wren51c75102013-07-16 20:49:17 -0400515 }
516
Christoph Studera7fe6312014-06-27 19:32:44 +0200517 /**
518 * Resets this view so it can be re-used for an updated notification.
519 */
Christoph Studer22f2ee52014-07-29 22:57:21 +0200520 @Override
Christoph Studera7fe6312014-06-27 19:32:44 +0200521 public void reset() {
Christoph Studer22f2ee52014-07-29 22:57:21 +0200522 super.reset();
Chris Wren78403d72014-07-28 10:23:24 +0100523 final boolean wasExpanded = isExpanded();
Selim Cinek379ff8f2015-02-20 17:03:16 +0100524 mMaxViewHeight = 0;
Christoph Studera7fe6312014-06-27 19:32:44 +0200525 mExpandable = false;
526 mHasUserChangedExpansion = false;
527 mUserLocked = false;
528 mShowingPublic = false;
Jorim Jaggiae441282014-08-01 02:45:18 +0200529 mSensitive = false;
530 mShowingPublicInitialized = false;
Christoph Studera7fe6312014-06-27 19:32:44 +0200531 mIsSystemExpanded = false;
Selim Cinek83bc7832015-10-22 13:26:54 -0700532 mOnKeyguard = false;
Selim Cinek1a521f32014-11-03 17:39:29 +0100533 mPublicLayout.reset(mIsHeadsUp);
534 mPrivateLayout.reset(mIsHeadsUp);
Selim Cinek31094df2014-08-14 19:28:15 +0200535 resetHeight();
536 logExpansionEvent(false, wasExpanded);
537 }
538
539 public void resetHeight() {
Selim Cinek1a521f32014-11-03 17:39:29 +0100540 if (mIsHeadsUp) {
541 resetActualHeight();
542 }
Christoph Studera7fe6312014-06-27 19:32:44 +0200543 mMaxExpandHeight = 0;
Selim Cinek8d490d42015-04-10 00:05:50 -0700544 mHeadsUpHeight = 0;
Selim Cineka5e211b2014-08-11 17:35:48 +0200545 mWasReset = true;
Selim Cinek31094df2014-08-14 19:28:15 +0200546 onHeightReset();
Selim Cinek6e28a672014-09-05 14:43:28 +0200547 requestLayout();
Christoph Studera7fe6312014-06-27 19:32:44 +0200548 }
549
Jorim Jaggi251957d2014-04-09 04:24:09 +0200550 @Override
551 protected void onFinishInflate() {
552 super.onFinishInflate();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200553 mPublicLayout = (NotificationContentView) findViewById(R.id.expandedPublic);
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800554 mPublicLayout.setContainingNotification(this);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200555 mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded);
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800556 mPrivateLayout.setExpandClickListener(mExpandClickListener);
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800557 mPrivateLayout.setContainingNotification(this);
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800558 mPublicLayout.setExpandClickListener(mExpandClickListener);
Selim Cinekab29aeb2015-02-20 18:18:32 +0100559 mGutsStub = (ViewStub) findViewById(R.id.notification_guts_stub);
560 mGutsStub.setOnInflateListener(new ViewStub.OnInflateListener() {
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200561 @Override
562 public void onInflate(ViewStub stub, View inflated) {
563 mGuts = (NotificationGuts) inflated;
564 mGuts.setClipTopAmount(getClipTopAmount());
565 mGuts.setActualHeight(getActualHeight());
Selim Cinekab29aeb2015-02-20 18:18:32 +0100566 mGutsStub = null;
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200567 }
568 });
Selim Cinekb5605e52015-02-20 18:21:41 +0100569 mChildrenContainerStub = (ViewStub) findViewById(R.id.child_container_stub);
570 mChildrenContainerStub.setOnInflateListener(new ViewStub.OnInflateListener() {
571
572 @Override
573 public void onInflate(ViewStub stub, View inflated) {
574 mChildrenContainer = (NotificationChildrenContainer) inflated;
Selim Cinek388df6d2015-10-22 13:25:11 -0700575 mChildrenContainer.setNotificationParent(ExpandableNotificationRow.this);
Selim Cinekb5605e52015-02-20 18:21:41 +0100576 }
577 });
Selim Cinek863834b2014-05-20 04:20:25 +0200578 mVetoButton = findViewById(R.id.veto);
Jorim Jaggife40f7d2014-04-28 15:20:04 +0200579 }
580
Selim Cinekab29aeb2015-02-20 18:18:32 +0100581 public void inflateGuts() {
582 if (mGuts == null) {
583 mGutsStub.inflate();
584 }
585 }
586
Selim Cinekda42d652015-12-04 15:51:16 -0800587 private void updateChildrenVisibility() {
Selim Cinekd84a5932015-12-15 11:45:36 -0800588 mPrivateLayout.setVisibility(!mShowingPublic && !mIsSummaryWithChildren ? VISIBLE
589 : INVISIBLE);
Selim Cinekef5127e2015-12-21 16:55:58 -0800590 if (mChildrenContainer != null) {
591 mChildrenContainer.setVisibility(!mShowingPublic && mIsSummaryWithChildren ? VISIBLE
592 : INVISIBLE);
Selim Cinekb5605e52015-02-20 18:21:41 +0100593 }
Selim Cinekef5127e2015-12-21 16:55:58 -0800594 if (mNotificationHeader != null) {
595 mNotificationHeader.setVisibility(!mShowingPublic && mIsSummaryWithChildren ? VISIBLE
596 : INVISIBLE);
597 }
Selim Cinekda42d652015-12-04 15:51:16 -0800598 // The limits might have changed if the view suddenly became a group or vice versa
599 updateLimits();
Selim Cinekb5605e52015-02-20 18:21:41 +0100600 }
601
Jorim Jaggife40f7d2014-04-28 15:20:04 +0200602 @Override
Alan Viverettea54956a2015-01-07 16:05:02 -0800603 public boolean onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event) {
604 if (super.onRequestSendAccessibilityEventInternal(child, event)) {
Jorim Jaggife40f7d2014-04-28 15:20:04 +0200605 // Add a record for the entire layout since its content is somehow small.
606 // The event comes from a leaf view that is interacted with.
607 AccessibilityEvent record = AccessibilityEvent.obtain();
608 onInitializeAccessibilityEvent(record);
609 dispatchPopulateAccessibilityEvent(record);
610 event.appendRecord(record);
611 return true;
612 }
613 return false;
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200614 }
Chris Wren51c75102013-07-16 20:49:17 -0400615
John Spurlocke15452b2014-08-21 09:44:39 -0400616 @Override
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100617 public void setDark(boolean dark, boolean fade, long delay) {
618 super.setDark(dark, fade, delay);
John Spurlocke15452b2014-08-21 09:44:39 -0400619 final NotificationContentView showing = getShowingLayout();
620 if (showing != null) {
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100621 showing.setDark(dark, fade, delay);
John Spurlocke15452b2014-08-21 09:44:39 -0400622 }
Selim Cinek9c7712d2015-12-08 19:19:48 -0800623 if (mIsSummaryWithChildren) {
624 mChildrenContainer.setDark(dark, fade, delay);
625 mNotificationHeaderWrapper.setDark(dark, fade, delay);
626 }
John Spurlocke15452b2014-08-21 09:44:39 -0400627 }
628
Chris Wren51c75102013-07-16 20:49:17 -0400629 public boolean isExpandable() {
Selim Cinek388df6d2015-10-22 13:25:11 -0700630 if (mIsSummaryWithChildren && !mShowingPublic) {
631 return !mChildrenExpanded;
632 }
Chris Wren51c75102013-07-16 20:49:17 -0400633 return mExpandable;
634 }
635
636 public void setExpandable(boolean expandable) {
637 mExpandable = expandable;
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800638 mPrivateLayout.updateExpandButtons(isExpandable());
Chris Wren51c75102013-07-16 20:49:17 -0400639 }
640
Selim Cinek4ffd6362015-12-29 15:12:23 +0100641 @Override
642 public void setClipToActualHeight(boolean clipToActualHeight) {
643 super.setClipToActualHeight(clipToActualHeight);
644 getShowingLayout().setClipToActualHeight(clipToActualHeight);
645 }
646
Selim Cinek1685e632014-04-08 02:27:49 +0200647 /**
648 * @return whether the user has changed the expansion state
649 */
650 public boolean hasUserChangedExpansion() {
651 return mHasUserChangedExpansion;
652 }
653
Chris Wren51c75102013-07-16 20:49:17 -0400654 public boolean isUserExpanded() {
655 return mUserExpanded;
656 }
657
Selim Cinek1685e632014-04-08 02:27:49 +0200658 /**
659 * Set this notification to be expanded by the user
660 *
661 * @param userExpanded whether the user wants this notification to be expanded
662 */
Chris Wren51c75102013-07-16 20:49:17 -0400663 public void setUserExpanded(boolean userExpanded) {
Selim Cinek388df6d2015-10-22 13:25:11 -0700664 setUserExpanded(userExpanded, false /* allowChildExpansion */);
665 }
666
667 /**
668 * Set this notification to be expanded by the user
669 *
670 * @param userExpanded whether the user wants this notification to be expanded
671 * @param allowChildExpansion whether a call to this method allows expanding children
672 */
673 public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700674 mFalsingManager.setNotificationExpanded();
Selim Cinek388df6d2015-10-22 13:25:11 -0700675 if (mIsSummaryWithChildren && !mShowingPublic && allowChildExpansion) {
676 mGroupManager.setGroupExpanded(mStatusBarNotification, userExpanded);
677 return;
678 }
Christoph Studera7fe6312014-06-27 19:32:44 +0200679 if (userExpanded && !mExpandable) return;
Chris Wren78403d72014-07-28 10:23:24 +0100680 final boolean wasExpanded = isExpanded();
Selim Cinek1685e632014-04-08 02:27:49 +0200681 mHasUserChangedExpansion = true;
Chris Wren51c75102013-07-16 20:49:17 -0400682 mUserExpanded = userExpanded;
Chris Wren78403d72014-07-28 10:23:24 +0100683 logExpansionEvent(true, wasExpanded);
Chris Wren51c75102013-07-16 20:49:17 -0400684 }
685
Selim Cinekccd14fb2014-08-12 18:53:24 +0200686 public void resetUserExpansion() {
687 mHasUserChangedExpansion = false;
688 mUserExpanded = false;
689 }
690
Chris Wren51c75102013-07-16 20:49:17 -0400691 public boolean isUserLocked() {
692 return mUserLocked;
693 }
694
695 public void setUserLocked(boolean userLocked) {
696 mUserLocked = userLocked;
697 }
698
Selim Cinek1685e632014-04-08 02:27:49 +0200699 /**
700 * @return has the system set this notification to be expanded
701 */
702 public boolean isSystemExpanded() {
703 return mIsSystemExpanded;
704 }
705
706 /**
707 * Set this notification to be expanded by the system.
708 *
709 * @param expand whether the system wants this notification to be expanded.
710 */
711 public void setSystemExpanded(boolean expand) {
Selim Cinek31094df2014-08-14 19:28:15 +0200712 if (expand != mIsSystemExpanded) {
713 final boolean wasExpanded = isExpanded();
714 mIsSystemExpanded = expand;
Selim Cinekb5605e52015-02-20 18:21:41 +0100715 notifyHeightChanged(false /* needsAnimation */);
Selim Cinek31094df2014-08-14 19:28:15 +0200716 logExpansionEvent(false, wasExpanded);
Selim Cinek83bc7832015-10-22 13:26:54 -0700717 if (mChildrenContainer != null) {
718 mChildrenContainer.updateGroupOverflow();
719 }
Selim Cinek31094df2014-08-14 19:28:15 +0200720 }
Jorim Jaggidce3c4c2014-04-29 23:12:24 +0200721 }
722
723 /**
Selim Cinek83bc7832015-10-22 13:26:54 -0700724 * @param onKeyguard whether to prevent notification expansion
Jorim Jaggidce3c4c2014-04-29 23:12:24 +0200725 */
Selim Cinek83bc7832015-10-22 13:26:54 -0700726 public void setOnKeyguard(boolean onKeyguard) {
727 if (onKeyguard != mOnKeyguard) {
Selim Cinek31094df2014-08-14 19:28:15 +0200728 final boolean wasExpanded = isExpanded();
Selim Cinek83bc7832015-10-22 13:26:54 -0700729 mOnKeyguard = onKeyguard;
Selim Cinek31094df2014-08-14 19:28:15 +0200730 logExpansionEvent(false, wasExpanded);
731 if (wasExpanded != isExpanded()) {
Selim Cinek2108fe02015-12-10 12:56:13 -0800732 if (mIsSummaryWithChildren) {
733 mChildrenContainer.updateGroupOverflow();
734 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100735 notifyHeightChanged(false /* needsAnimation */);
Selim Cinek31094df2014-08-14 19:28:15 +0200736 }
737 }
Selim Cinek1685e632014-04-08 02:27:49 +0200738 }
739
740 /**
Dan Sandler0d3e62f2014-07-14 17:13:50 -0400741 * @return Can the underlying notification be cleared?
742 */
743 public boolean isClearable() {
Selim Cineka37774f2014-11-11 19:16:18 +0100744 return mStatusBarNotification != null && mStatusBarNotification.isClearable();
Dan Sandler0d3e62f2014-07-14 17:13:50 -0400745 }
746
747 /**
Selim Cinek1685e632014-04-08 02:27:49 +0200748 * Apply an expansion state to the layout.
Selim Cinek1685e632014-04-08 02:27:49 +0200749 */
Jorim Jaggidce3c4c2014-04-29 23:12:24 +0200750 public void applyExpansionToLayout() {
751 boolean expand = isExpanded();
Chris Wren51c75102013-07-16 20:49:17 -0400752 if (expand && mExpandable) {
Selim Cinekeef84282015-10-30 16:28:00 -0700753 setActualHeight(mMaxExpandHeight);
Chris Wren51c75102013-07-16 20:49:17 -0400754 } else {
Selim Cinek860b6da2015-12-16 19:02:19 -0800755 setActualHeight(getMinHeight());
Chris Wren51c75102013-07-16 20:49:17 -0400756 }
Chris Wren51c75102013-07-16 20:49:17 -0400757 }
Dan Sandlera5e0f412014-01-23 15:11:54 -0500758
Jorim Jaggi9cbadd32014-05-01 20:18:31 +0200759 @Override
760 public int getIntrinsicHeight() {
Jorim Jaggibe565df2014-04-28 17:51:23 +0200761 if (isUserLocked()) {
762 return getActualHeight();
763 }
Selim Cinekd84a5932015-12-15 11:45:36 -0800764 if (mGuts != null && mGuts.areGutsExposed()) {
765 return mGuts.getHeight();
766 } else if ((isChildInGroup() && !isGroupExpanded())) {
767 return mPrivateLayout.getMinHeight();
768 } else if (mSensitive && mHideSensitiveForIntrinsicHeight) {
769 return getMinHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -0700770 } else if (mIsSummaryWithChildren && !mOnKeyguard) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800771 return mChildrenContainer.getIntrinsicHeight();
Selim Cinek60122be2015-04-15 18:16:50 -0700772 } else if (mIsHeadsUp) {
Selim Cinek31aada42015-12-18 17:51:15 -0800773 if (isPinned()) {
774 return getPinnedHeadsUpHeight(true /* atLeastMinHeight */);
775 } else if (isExpanded()) {
Selim Cinekd84a5932015-12-15 11:45:36 -0800776 return Math.max(getMaxExpandHeight(), mHeadsUpHeight);
Selim Cinek8d490d42015-04-10 00:05:50 -0700777 } else {
Selim Cinekd84a5932015-12-15 11:45:36 -0800778 return Math.max(getMinHeight(), mHeadsUpHeight);
Selim Cinek8d490d42015-04-10 00:05:50 -0700779 }
Selim Cinek31aada42015-12-18 17:51:15 -0800780 } else if (isExpanded()) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700781 return getMaxExpandHeight();
Selim Cinekd84a5932015-12-15 11:45:36 -0800782 } else {
783 return getMinHeight();
Selim Cinek1685e632014-04-08 02:27:49 +0200784 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100785 }
Selim Cinek1685e632014-04-08 02:27:49 +0200786
Selim Cinekeef84282015-10-30 16:28:00 -0700787 private boolean isGroupExpanded() {
788 return mGroupManager.isGroupExpanded(mStatusBarNotification);
Selim Cinekb5605e52015-02-20 18:21:41 +0100789 }
790
Selim Cinekeef84282015-10-30 16:28:00 -0700791 /**
792 * @return whether this view has a header on the top of the content
793 */
794 private boolean hasNotificationHeader() {
795 return mIsSummaryWithChildren;
Selim Cinek1685e632014-04-08 02:27:49 +0200796 }
797
Selim Cinek263398f2015-10-21 17:40:23 -0700798 private void onChildrenCountChanged() {
Selim Cinek8fc93c92015-11-23 17:48:07 -0800799 mIsSummaryWithChildren = BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS
Selim Cinek263398f2015-10-21 17:40:23 -0700800 && mGroupManager.hasGroupChildren(mStatusBarNotification);
Selim Cinek8fc93c92015-11-23 17:48:07 -0800801 if (mIsSummaryWithChildren) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800802 if (mChildrenContainer == null) {
803 mChildrenContainerStub.inflate();
804 }
Selim Cinek8fc93c92015-11-23 17:48:07 -0800805 if (mNotificationHeader == null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800806 recreateNotificationHeader();
807 }
Selim Cinek263398f2015-10-21 17:40:23 -0700808 }
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800809 mPrivateLayout.updateExpandButtons(isExpandable());
Selim Cinekea4bef72015-12-02 15:51:10 -0800810 updateChildrenHeaderAppearance();
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800811 updateHeaderChildCount();
Selim Cinekda42d652015-12-04 15:51:16 -0800812 updateChildrenVisibility();
Selim Cinek263398f2015-10-21 17:40:23 -0700813 }
814
Selim Cinek1685e632014-04-08 02:27:49 +0200815 /**
816 * Check whether the view state is currently expanded. This is given by the system in {@link
817 * #setSystemExpanded(boolean)} and can be overridden by user expansion or
818 * collapsing in {@link #setUserExpanded(boolean)}. Note that the visual appearance of this
819 * view can differ from this state, if layout params are modified from outside.
820 *
821 * @return whether the view state is currently expanded.
822 */
Selim Cinek83bc7832015-10-22 13:26:54 -0700823 public boolean isExpanded() {
824 return !mOnKeyguard
Selim Cinekb5605e52015-02-20 18:21:41 +0100825 && (!hasUserChangedExpansion() && (isSystemExpanded() || isSystemChildExpanded())
826 || isUserExpanded());
827 }
828
829 private boolean isSystemChildExpanded() {
830 return mIsSystemChildExpanded;
831 }
832
833 public void setSystemChildExpanded(boolean expanded) {
834 mIsSystemChildExpanded = expanded;
Selim Cinek1685e632014-04-08 02:27:49 +0200835 }
836
837 @Override
838 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
839 super.onLayout(changed, left, top, right, bottom);
Selim Cineka5e211b2014-08-11 17:35:48 +0200840 boolean updateExpandHeight = mMaxExpandHeight == 0 && !mWasReset;
Selim Cinek8d490d42015-04-10 00:05:50 -0700841 updateMaxHeights();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200842 if (updateExpandHeight) {
Jorim Jaggidce3c4c2014-04-29 23:12:24 +0200843 applyExpansionToLayout();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200844 }
Selim Cineka5e211b2014-08-11 17:35:48 +0200845 mWasReset = false;
Selim Cinek1685e632014-04-08 02:27:49 +0200846 }
847
Selim Cinek8d490d42015-04-10 00:05:50 -0700848 private void updateMaxHeights() {
Selim Cinekd2319fb2014-09-01 19:41:54 +0200849 int intrinsicBefore = getIntrinsicHeight();
Selim Cinek8d490d42015-04-10 00:05:50 -0700850 View expandedChild = mPrivateLayout.getExpandedChild();
851 if (expandedChild == null) {
852 expandedChild = mPrivateLayout.getContractedChild();
853 }
854 mMaxExpandHeight = expandedChild.getHeight();
855 View headsUpChild = mPrivateLayout.getHeadsUpChild();
Selim Cinek1f3f5442015-04-10 17:54:46 -0700856 if (headsUpChild == null) {
857 headsUpChild = mPrivateLayout.getContractedChild();
Selim Cinek8d490d42015-04-10 00:05:50 -0700858 }
Selim Cinek1f3f5442015-04-10 17:54:46 -0700859 mHeadsUpHeight = headsUpChild.getHeight();
Selim Cinekd2319fb2014-09-01 19:41:54 +0200860 if (intrinsicBefore != getIntrinsicHeight()) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100861 notifyHeightChanged(false /* needsAnimation */);
Selim Cinekd2319fb2014-09-01 19:41:54 +0200862 }
863 }
864
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800865 @Override
866 public void notifyHeightChanged(boolean needsAnimation) {
867 super.notifyHeightChanged(needsAnimation);
868 getShowingLayout().requestSelectLayout(needsAnimation || isUserLocked());
869 }
870
Jorim Jaggiae441282014-08-01 02:45:18 +0200871 public void setSensitive(boolean sensitive) {
872 mSensitive = sensitive;
873 }
874
875 public void setHideSensitiveForIntrinsicHeight(boolean hideSensitive) {
Selim Cinek60122be2015-04-15 18:16:50 -0700876 mHideSensitiveForIntrinsicHeight = hideSensitive;
Jorim Jaggiae441282014-08-01 02:45:18 +0200877 }
878
879 public void setHideSensitive(boolean hideSensitive, boolean animated, long delay,
880 long duration) {
881 boolean oldShowingPublic = mShowingPublic;
882 mShowingPublic = mSensitive && hideSensitive;
883 if (mShowingPublicInitialized && mShowingPublic == oldShowingPublic) {
884 return;
885 }
Dan Sandlera5e0f412014-01-23 15:11:54 -0500886
887 // bail out if no public version
Selim Cinek1685e632014-04-08 02:27:49 +0200888 if (mPublicLayout.getChildCount() == 0) return;
Dan Sandlera5e0f412014-01-23 15:11:54 -0500889
Jorim Jaggiae441282014-08-01 02:45:18 +0200890 if (!animated) {
891 mPublicLayout.animate().cancel();
892 mPrivateLayout.animate().cancel();
893 mPublicLayout.setAlpha(1f);
894 mPrivateLayout.setAlpha(1f);
895 mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE);
Selim Cinekd84a5932015-12-15 11:45:36 -0800896 updateChildrenVisibility();
Jorim Jaggiae441282014-08-01 02:45:18 +0200897 } else {
898 animateShowingPublic(delay, duration);
899 }
Dan Sandler0d3e62f2014-07-14 17:13:50 -0400900
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800901 mPrivateLayout.updateExpandButtons(isExpandable());
Dan Sandler0d3e62f2014-07-14 17:13:50 -0400902 updateVetoButton();
Jorim Jaggiae441282014-08-01 02:45:18 +0200903 mShowingPublicInitialized = true;
904 }
905
906 private void animateShowingPublic(long delay, long duration) {
Selim Cinekd84a5932015-12-15 11:45:36 -0800907 View[] privateViews = mIsSummaryWithChildren ?
908 new View[] {mChildrenContainer, mNotificationHeader}
909 : new View[] {mPrivateLayout};
910 View[] publicViews = new View[] {mPublicLayout};
911 View[] hiddenChildren = mShowingPublic ? privateViews : publicViews;
912 View[] shownChildren = mShowingPublic ? publicViews : privateViews;
913 for (final View hiddenView : hiddenChildren) {
914 hiddenView.setVisibility(View.VISIBLE);
915 hiddenView.animate().cancel();
916 hiddenView.animate()
917 .alpha(0f)
918 .setStartDelay(delay)
919 .setDuration(duration)
920 .withEndAction(new Runnable() {
921 @Override
922 public void run() {
923 hiddenView.setVisibility(View.INVISIBLE);
924 }
925 });
926 }
927 for (View showView : shownChildren) {
928 showView.setVisibility(View.VISIBLE);
929 showView.setAlpha(0f);
930 showView.animate().cancel();
931 showView.animate()
932 .alpha(1f)
933 .setStartDelay(delay)
934 .setDuration(duration);
935 }
Dan Sandler0d3e62f2014-07-14 17:13:50 -0400936 }
937
938 private void updateVetoButton() {
939 // public versions cannot be dismissed
940 mVetoButton.setVisibility(isClearable() && !mShowingPublic ? View.VISIBLE : View.GONE);
Dan Sandlera5e0f412014-01-23 15:11:54 -0500941 }
Jorim Jaggi251957d2014-04-09 04:24:09 +0200942
Selim Cinekb5605e52015-02-20 18:21:41 +0100943 public void setChildrenExpanded(boolean expanded, boolean animate) {
944 mChildrenExpanded = expanded;
Selim Cinek7b836392015-12-04 20:02:59 -0800945 if (mNotificationHeader != null) {
946 mNotificationHeader.setExpanded(expanded);
947 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700948 if (mChildrenContainer != null) {
949 mChildrenContainer.setChildrenExpanded(expanded);
950 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100951 }
952
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800953 public void updateHeaderChildCount() {
954 if (mIsSummaryWithChildren) {
955 mNotificationHeader.setChildCount(
956 mChildrenContainer.getNotificationChildren().size());
Selim Cinekb5605e52015-02-20 18:21:41 +0100957 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100958 }
959
960 public static void applyTint(View v, int color) {
961 int alpha;
962 if (color != 0) {
963 alpha = COLORED_DIVIDER_ALPHA;
964 } else {
965 color = 0xff000000;
966 alpha = DEFAULT_DIVIDER_ALPHA;
967 }
968 if (v.getBackground() instanceof ColorDrawable) {
969 ColorDrawable background = (ColorDrawable) v.getBackground();
970 background.mutate();
971 background.setColor(color);
972 background.setAlpha(alpha);
973 }
974 }
975
Selim Cinek1685e632014-04-08 02:27:49 +0200976 public int getMaxExpandHeight() {
Selim Cinekb5605e52015-02-20 18:21:41 +0100977 return mMaxExpandHeight;
Chris Wren51c75102013-07-16 20:49:17 -0400978 }
Jorim Jaggi584a7aa2014-04-10 23:26:13 +0200979
Jorim Jaggibe565df2014-04-28 17:51:23 +0200980 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200981 public boolean isContentExpandable() {
Selim Cinek2f0df8a2014-06-10 17:40:42 +0200982 NotificationContentView showingLayout = getShowingLayout();
983 return showingLayout.isContentExpandable();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200984 }
985
986 @Override
Selim Cinek560e64d2015-06-09 19:58:11 -0700987 protected View getContentView() {
988 return getShowingLayout();
989 }
990
991 @Override
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200992 public void setActualHeight(int height, boolean notifyListeners) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100993 super.setActualHeight(height, notifyListeners);
Selim Cinekeef84282015-10-30 16:28:00 -0700994 int contentHeight = Math.max(getMinHeight(), height);
Selim Cinekb5605e52015-02-20 18:21:41 +0100995 mPrivateLayout.setContentHeight(contentHeight);
996 mPublicLayout.setContentHeight(contentHeight);
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200997 if (mGuts != null) {
998 mGuts.setActualHeight(height);
999 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001000 invalidate();
Jorim Jaggibe565df2014-04-28 17:51:23 +02001001 }
1002
1003 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01001004 public int getMaxContentHeight() {
Selim Cinek83bc7832015-10-22 13:26:54 -07001005 if (mIsSummaryWithChildren && !mShowingPublic) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001006 return mChildrenContainer.getMaxContentHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -07001007 }
Selim Cinek2f0df8a2014-06-10 17:40:42 +02001008 NotificationContentView showingLayout = getShowingLayout();
1009 return showingLayout.getMaxHeight();
Jorim Jaggibe565df2014-04-28 17:51:23 +02001010 }
1011
1012 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001013 public int getMinHeight() {
Selim Cinek31aada42015-12-18 17:51:15 -08001014 if (mIsHeadsUp && mHeadsUpManager.isTrackingHeadsUp()) {
1015 return getPinnedHeadsUpHeight(false /* atLeastMinHeight */);
1016 } else if (mIsSummaryWithChildren && !isGroupExpanded() && !mShowingPublic) {
Selim Cinekb55386d2015-12-16 17:26:49 -08001017 return mChildrenContainer.getMinHeight();
Selim Cinek31aada42015-12-18 17:51:15 -08001018 } else if (mIsHeadsUp) {
1019 return mHeadsUpHeight;
Selim Cinekb55386d2015-12-16 17:26:49 -08001020 }
Selim Cinek816c8e42015-11-19 12:00:45 -08001021 NotificationContentView showingLayout = getShowingLayout();
1022 return showingLayout.getMinHeight();
1023 }
1024
1025 @Override
1026 public int getMinExpandHeight() {
Selim Cinek83bc7832015-10-22 13:26:54 -07001027 if (mIsSummaryWithChildren && !mOnKeyguard) {
Selim Cinekb55386d2015-12-16 17:26:49 -08001028 return mChildrenContainer.getMinExpandHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -07001029 }
Selim Cinek816c8e42015-11-19 12:00:45 -08001030 return getMinHeight();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001031 }
1032
1033 @Override
Jorim Jaggibe565df2014-04-28 17:51:23 +02001034 public void setClipTopAmount(int clipTopAmount) {
1035 super.setClipTopAmount(clipTopAmount);
1036 mPrivateLayout.setClipTopAmount(clipTopAmount);
Selim Cinek2f0df8a2014-06-10 17:40:42 +02001037 mPublicLayout.setClipTopAmount(clipTopAmount);
Jorim Jaggib1cd3c12014-09-08 19:55:17 +02001038 if (mGuts != null) {
1039 mGuts.setClipTopAmount(clipTopAmount);
1040 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001041 }
1042
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001043 private void recreateNotificationHeader() {
1044 final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(),
1045 getStatusBarNotification().getNotification());
1046 final RemoteViews header = builder.makeNotificationHeader();
1047 if (mNotificationHeader == null) {
1048 mNotificationHeader = (NotificationHeaderView) header.apply(getContext(), this);
1049 final View expandButton = mNotificationHeader.findViewById(
1050 com.android.internal.R.id.expand_button);
1051 expandButton.setVisibility(VISIBLE);
1052 mNotificationHeader.setOnClickListener(mExpandClickListener);
Selim Cinek9c7712d2015-12-08 19:19:48 -08001053 mNotificationHeaderWrapper = NotificationViewWrapper.wrap(getContext(),
1054 mNotificationHeader);
Selim Cinekb5a83612015-12-11 14:14:39 -08001055 addView(mNotificationHeader, indexOfChild(mChildrenContainer) + 1);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001056 } else {
1057 header.reapply(getContext(), mNotificationHeader);
Selim Cinek4ffd6362015-12-29 15:12:23 +01001058 mNotificationHeaderWrapper.notifyContentUpdated(mEntry.notification);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001059 }
Selim Cinek7b836392015-12-04 20:02:59 -08001060 updateHeaderExpandButton();
Selim Cinekea4bef72015-12-02 15:51:10 -08001061 updateChildrenHeaderAppearance();
Selim Cinek8fc93c92015-11-23 17:48:07 -08001062 updateHeaderChildCount();
Selim Cinek343e6e22014-04-11 21:23:30 +02001063 }
Selim Cinek7d447722014-06-10 15:51:59 +02001064
Selim Cinek7b836392015-12-04 20:02:59 -08001065 private void updateHeaderExpandButton() {
1066 if (mIsSummaryWithChildren) {
1067 mNotificationHeader.setIsGroupHeader(true /* isGroupHeader*/);
1068 }
1069 }
1070
Selim Cinekea4bef72015-12-02 15:51:10 -08001071 public void updateChildrenHeaderAppearance() {
1072 if (mIsSummaryWithChildren) {
1073 mHeaderUtil.updateChildrenHeaderAppearance();
1074 }
1075 }
1076
Selim Cinek31094df2014-08-14 19:28:15 +02001077 public boolean isMaxExpandHeightInitialized() {
1078 return mMaxExpandHeight != 0;
Selim Cinek7d447722014-06-10 15:51:59 +02001079 }
Selim Cinek2f0df8a2014-06-10 17:40:42 +02001080
1081 private NotificationContentView getShowingLayout() {
1082 return mShowingPublic ? mPublicLayout : mPrivateLayout;
1083 }
Chris Wren78403d72014-07-28 10:23:24 +01001084
Jorim Jaggi59ec3042015-06-05 15:18:43 -07001085 @Override
1086 public void setShowingLegacyBackground(boolean showing) {
1087 super.setShowingLegacyBackground(showing);
1088 mPrivateLayout.setShowingLegacyBackground(showing);
1089 mPublicLayout.setShowingLegacyBackground(showing);
1090 }
1091
Selim Cineka6c6bfb2015-10-29 16:27:08 -07001092 @Override
1093 protected void updateBackgroundTint() {
1094 super.updateBackgroundTint();
1095 updateNoBackgroundState();
1096 if (mIsSummaryWithChildren) {
1097 List<ExpandableNotificationRow> notificationChildren =
1098 mChildrenContainer.getNotificationChildren();
1099 for (int i = 0; i < notificationChildren.size(); i++) {
1100 ExpandableNotificationRow child = notificationChildren.get(i);
1101 child.updateNoBackgroundState();
1102 }
1103 }
1104 }
1105
1106 private void updateNoBackgroundState() {
1107 mShowNoBackground = isChildInGroup() && hasSameBgColor(mNotificationParent);
1108 updateBackground();
1109 }
1110
Chris Wren78403d72014-07-28 10:23:24 +01001111 public void setExpansionLogger(ExpansionLogger logger, String key) {
1112 mLogger = logger;
1113 mLoggingKey = key;
1114 }
1115
Chris Wren78403d72014-07-28 10:23:24 +01001116 private void logExpansionEvent(boolean userAction, boolean wasExpanded) {
1117 final boolean nowExpanded = isExpanded();
1118 if (wasExpanded != nowExpanded && mLogger != null) {
1119 mLogger.logNotificationExpansion(mLoggingKey, userAction, nowExpanded) ;
1120 }
1121 }
Selim Cinek570981d2015-12-01 11:37:01 -08001122
1123 public interface OnExpandClickListener {
Selim Cinek31aada42015-12-18 17:51:15 -08001124 void onExpandClicked(NotificationData.Entry clickedEntry, boolean nowExpanded);
Selim Cinek570981d2015-12-01 11:37:01 -08001125 }
Chris Wren51c75102013-07-16 20:49:17 -04001126}