blob: 3f88ff97337cbdb22c006886e21b3461e699ed01 [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
Mady Mellor4b80b102016-01-22 08:03:58 -080019import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
Mady Mellor4b80b102016-01-22 08:03:58 -080021import android.animation.ObjectAnimator;
Mady Mellor4b80b102016-01-22 08:03:58 -080022import android.animation.ValueAnimator.AnimatorUpdateListener;
Selim Cinekddf1b392016-05-27 16:33:10 -070023import android.annotation.Nullable;
Chris Wren51c75102013-07-16 20:49:17 -040024import android.content.Context;
Selim Cinekcab4a602014-09-03 14:47:57 +020025import android.graphics.drawable.AnimatedVectorDrawable;
26import android.graphics.drawable.AnimationDrawable;
Selim Cinekb5605e52015-02-20 18:21:41 +010027import android.graphics.drawable.ColorDrawable;
Selim Cinekcab4a602014-09-03 14:47:57 +020028import android.graphics.drawable.Drawable;
Selim Cinekda42d652015-12-04 15:51:16 -080029import android.os.Build;
Selim Cineke9bad242016-06-15 11:46:37 -070030import android.os.Bundle;
Jorim Jaggib1cd3c12014-09-08 19:55:17 +020031import android.service.notification.StatusBarNotification;
Chris Wren51c75102013-07-16 20:49:17 -040032import android.util.AttributeSet;
Mady Mellorb0a82462016-04-30 17:31:02 -070033import android.util.FloatProperty;
34import android.util.Property;
Selim Cinek01af3342016-02-09 19:25:31 -080035import android.view.LayoutInflater;
Selim Cinek6183d122016-01-14 18:48:41 -080036import android.view.MotionEvent;
Selim Cinekeaa29ca2015-11-23 13:51:13 -080037import android.view.NotificationHeaderView;
Dan Sandlera5e0f412014-01-23 15:11:54 -050038import android.view.View;
Selim Cinek875a3a12016-11-18 17:52:16 -080039import android.view.ViewGroup;
Jorim Jaggib1cd3c12014-09-08 19:55:17 +020040import android.view.ViewStub;
Jorim Jaggife40f7d2014-04-28 15:20:04 +020041import android.view.accessibility.AccessibilityEvent;
Selim Cineke9bad242016-06-15 11:46:37 -070042import android.view.accessibility.AccessibilityNodeInfo;
Selim Cinek98713a42015-09-21 15:47:20 +020043import android.widget.Chronometer;
Selim Cinekcab4a602014-09-03 14:47:57 +020044import android.widget.ImageView;
Selim Cinekb5605e52015-02-20 18:21:41 +010045
Chris Wren698b1702016-05-23 11:16:32 -040046import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010047import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Selim Cinek4bb59342016-04-08 19:29:35 -070048import com.android.internal.util.NotificationColorUtil;
Selim Cinek875a3a12016-11-18 17:52:16 -080049import com.android.internal.widget.CachingIconView;
Selim Cinek0242fbb2016-10-19 13:38:32 -070050import com.android.systemui.Interpolators;
Dan Sandlera5e0f412014-01-23 15:11:54 -050051import com.android.systemui.R;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070052import com.android.systemui.classifier.FalsingManager;
Selim Cinekc897bd32016-03-18 17:32:31 -070053import com.android.systemui.statusbar.notification.HybridNotificationView;
Selim Cineka7d4f822016-12-06 14:34:47 -080054import com.android.systemui.statusbar.notification.VisualStabilityManager;
Selim Cinekb5605e52015-02-20 18:21:41 +010055import com.android.systemui.statusbar.phone.NotificationGroupManager;
Selim Cinek31aada42015-12-18 17:51:15 -080056import com.android.systemui.statusbar.policy.HeadsUpManager;
Selim Cinek0cfbef42016-11-09 19:06:36 -080057import com.android.systemui.statusbar.stack.AnimationProperties;
58import com.android.systemui.statusbar.stack.ExpandableViewState;
Selim Cinekb5605e52015-02-20 18:21:41 +010059import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
Selim Cineke9bad242016-06-15 11:46:37 -070060import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
Selim Cinekb5605e52015-02-20 18:21:41 +010061import com.android.systemui.statusbar.stack.StackScrollState;
Selim Cinekb5605e52015-02-20 18:21:41 +010062
Mady Mellor4b80b102016-01-22 08:03:58 -080063import java.util.ArrayList;
Selim Cinekb5605e52015-02-20 18:21:41 +010064import java.util.List;
Dan Sandlera5e0f412014-01-23 15:11:54 -050065
Jorim Jaggi4222d9a2014-04-23 16:13:15 +020066public class ExpandableNotificationRow extends ActivatableNotificationView {
Selim Cinekb5605e52015-02-20 18:21:41 +010067
68 private static final int DEFAULT_DIVIDER_ALPHA = 0x29;
69 private static final int COLORED_DIVIDER_ALPHA = 0x7B;
Selim Cinek0242fbb2016-10-19 13:38:32 -070070 private int mIconTransformContentShift;
Selim Cinek875a3a12016-11-18 17:52:16 -080071 private int mIconTransformContentShiftNoIcon;
Selim Cinek01af3342016-02-09 19:25:31 -080072 private int mNotificationMinHeightLegacy;
73 private int mMaxHeadsUpHeightLegacy;
74 private int mMaxHeadsUpHeight;
75 private int mNotificationMinHeight;
76 private int mNotificationMaxHeight;
Adrian Roos0aac04f2016-12-08 15:59:29 -080077 private int mNotificationAmbientHeight;
Mady Mellorb0a82462016-04-30 17:31:02 -070078 private int mIncreasedPaddingBetweenElements;
Chris Wren51c75102013-07-16 20:49:17 -040079
Selim Cinek1685e632014-04-08 02:27:49 +020080 /** Does this row contain layouts that can adapt to row expansion */
Chris Wren51c75102013-07-16 20:49:17 -040081 private boolean mExpandable;
Selim Cinek1685e632014-04-08 02:27:49 +020082 /** Has the user actively changed the expansion state of this row */
83 private boolean mHasUserChangedExpansion;
84 /** If {@link #mHasUserChangedExpansion}, has the user expanded this row */
Chris Wren51c75102013-07-16 20:49:17 -040085 private boolean mUserExpanded;
Selim Cinek31aada42015-12-18 17:51:15 -080086
87 /**
88 * Has this notification been expanded while it was pinned
89 */
90 private boolean mExpandedWhenPinned;
Selim Cinek1685e632014-04-08 02:27:49 +020091 /** Is the user touching this row */
Chris Wren51c75102013-07-16 20:49:17 -040092 private boolean mUserLocked;
Selim Cinek1685e632014-04-08 02:27:49 +020093 /** Are we showing the "public" version */
Dan Sandlera5e0f412014-01-23 15:11:54 -050094 private boolean mShowingPublic;
Jorim Jaggiae441282014-08-01 02:45:18 +020095 private boolean mSensitive;
Selim Cinek3c76d502016-02-19 15:16:33 -080096 private boolean mSensitiveHiddenInGeneral;
Jorim Jaggiae441282014-08-01 02:45:18 +020097 private boolean mShowingPublicInitialized;
Selim Cinek60122be2015-04-15 18:16:50 -070098 private boolean mHideSensitiveForIntrinsicHeight;
Chris Wren51c75102013-07-16 20:49:17 -040099
Selim Cinek1685e632014-04-08 02:27:49 +0200100 /**
101 * Is this notification expanded by the system. The expansion state can be overridden by the
102 * user expansion.
103 */
104 private boolean mIsSystemExpanded;
Jorim Jaggidce3c4c2014-04-29 23:12:24 +0200105
106 /**
Selim Cinek83bc7832015-10-22 13:26:54 -0700107 * Whether the notification is on the keyguard and the expansion is disabled.
Jorim Jaggidce3c4c2014-04-29 23:12:24 +0200108 */
Selim Cinek83bc7832015-10-22 13:26:54 -0700109 private boolean mOnKeyguard;
Jorim Jaggidce3c4c2014-04-29 23:12:24 +0200110
Mady Mellorb0a82462016-04-30 17:31:02 -0700111 private Animator mTranslateAnim;
Mady Mellor4b80b102016-01-22 08:03:58 -0800112 private ArrayList<View> mTranslateableViews;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200113 private NotificationContentView mPublicLayout;
114 private NotificationContentView mPrivateLayout;
Adrian Rooseb434ff2017-01-11 11:18:48 -0800115 private NotificationContentView[] mLayouts;
Selim Cinek1685e632014-04-08 02:27:49 +0200116 private int mMaxExpandHeight;
Selim Cinek8d490d42015-04-10 00:05:50 -0700117 private int mHeadsUpHeight;
Selim Cinek4bb59342016-04-08 19:29:35 -0700118 private int mNotificationColor;
Chris Wren78403d72014-07-28 10:23:24 +0100119 private ExpansionLogger mLogger;
120 private String mLoggingKey;
Mady Mellor761cde12017-01-10 11:36:39 -0800121 private NotificationMenuRow mMenuRow;
Selim Cinek8d490d42015-04-10 00:05:50 -0700122 private NotificationGuts mGuts;
Selim Cinekda42d652015-12-04 15:51:16 -0800123 private NotificationData.Entry mEntry;
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200124 private StatusBarNotification mStatusBarNotification;
Mady Mellor3fd273e2016-03-15 21:08:14 -0700125 private String mAppName;
Selim Cinek1a521f32014-11-03 17:39:29 +0100126 private boolean mIsHeadsUp;
Selim Cinek98713a42015-09-21 15:47:20 +0200127 private boolean mLastChronometerRunning = true;
Selim Cinekb5605e52015-02-20 18:21:41 +0100128 private ViewStub mChildrenContainerStub;
129 private NotificationGroupManager mGroupManager;
Selim Cinekb5605e52015-02-20 18:21:41 +0100130 private boolean mChildrenExpanded;
Selim Cinek263398f2015-10-21 17:40:23 -0700131 private boolean mIsSummaryWithChildren;
Selim Cinekb5605e52015-02-20 18:21:41 +0100132 private NotificationChildrenContainer mChildrenContainer;
Mady Mellor761cde12017-01-10 11:36:39 -0800133 private ViewStub mMenuRowStub;
Selim Cinekab29aeb2015-02-20 18:18:32 +0100134 private ViewStub mGutsStub;
Selim Cinekb5605e52015-02-20 18:21:41 +0100135 private boolean mIsSystemChildExpanded;
Selim Cinek684a4422015-04-15 16:18:39 -0700136 private boolean mIsPinned;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700137 private FalsingManager mFalsingManager;
Selim Cinek31aada42015-12-18 17:51:15 -0800138 private HeadsUpManager mHeadsUpManager;
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200139
Jorim Jaggi5eb67c22015-08-19 19:50:49 -0700140 private boolean mJustClicked;
Selim Cinek5a175d92015-11-23 18:01:33 -0800141 private boolean mIconAnimationRunning;
Selim Cinek34d93b02015-10-22 12:30:38 -0700142 private boolean mShowNoBackground;
Selim Cinek388df6d2015-10-22 13:25:11 -0700143 private ExpandableNotificationRow mNotificationParent;
Selim Cinek570981d2015-12-01 11:37:01 -0800144 private OnExpandClickListener mOnExpandClickListener;
Mady Mellorb0a82462016-04-30 17:31:02 -0700145 private boolean mGroupExpansionChanging;
146
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800147 private OnClickListener mExpandClickListener = new OnClickListener() {
148 @Override
149 public void onClick(View v) {
Selim Cinek624c02db2015-12-14 21:00:02 -0800150 if (!mShowingPublic && mGroupManager.isSummaryOfGroup(mStatusBarNotification)) {
Mady Mellor1a5d8ea2016-06-09 10:42:42 -0700151 mGroupExpansionChanging = true;
Chris Wren698b1702016-05-23 11:16:32 -0400152 final boolean wasExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
153 boolean nowExpanded = mGroupManager.toggleGroupExpansion(mStatusBarNotification);
154 mOnExpandClickListener.onExpandClicked(mEntry, nowExpanded);
Chris Wren698b1702016-05-23 11:16:32 -0400155 MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTIFICATION_GROUP_EXPANDER,
156 nowExpanded);
157 logExpansionEvent(true /* userAction */, wasExpanded);
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800158 } else {
Selim Cineke9bad242016-06-15 11:46:37 -0700159 if (v.isAccessibilityFocused()) {
160 mPrivateLayout.setFocusOnVisibilityChange();
161 }
Selim Cinek31aada42015-12-18 17:51:15 -0800162 boolean nowExpanded;
163 if (isPinned()) {
164 nowExpanded = !mExpandedWhenPinned;
165 mExpandedWhenPinned = nowExpanded;
166 } else {
167 nowExpanded = !isExpanded();
168 setUserExpanded(nowExpanded);
169 }
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800170 notifyHeightChanged(true);
Selim Cinek31aada42015-12-18 17:51:15 -0800171 mOnExpandClickListener.onExpandClicked(mEntry, nowExpanded);
Chris Wren698b1702016-05-23 11:16:32 -0400172 MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTIFICATION_EXPANDER,
173 nowExpanded);
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800174 }
175 }
176 };
Selim Cinek1b2a05e2016-04-28 14:20:39 -0700177 private boolean mForceUnlocked;
Selim Cinek3f19f602016-05-02 18:01:56 -0700178 private boolean mDismissed;
179 private boolean mKeepInParent;
180 private boolean mRemoved;
Mady Mellorb0a82462016-04-30 17:31:02 -0700181 private static final Property<ExpandableNotificationRow, Float> TRANSLATE_CONTENT =
182 new FloatProperty<ExpandableNotificationRow>("translate") {
183 @Override
184 public void setValue(ExpandableNotificationRow object, float value) {
185 object.setTranslation(value);
186 }
187
188 @Override
189 public Float get(ExpandableNotificationRow object) {
190 return object.getTranslation();
191 }
192 };
Selim Cinekddf1b392016-05-27 16:33:10 -0700193 private OnClickListener mOnClickListener;
Selim Cinek73cf02a2016-06-17 13:08:00 -0700194 private boolean mHeadsupDisappearRunning;
Selim Cineke9bad242016-06-15 11:46:37 -0700195 private View mChildAfterViewWhenDismissed;
196 private View mGroupParentWhenDismissed;
197 private boolean mRefocusOnDismiss;
Selim Cinek2b549f42016-11-22 16:38:51 -0800198 private float mContentTransformationAmount;
Selim Cinek0242fbb2016-10-19 13:38:32 -0700199 private boolean mIconsVisible = true;
Selim Cinekd127d792016-11-01 19:11:41 -0700200 private boolean mAboveShelf;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800201 private boolean mShowAmbient;
Selim Cinek875a3a12016-11-18 17:52:16 -0800202 private boolean mIsLastChild;
Selim Cineke9079112016-12-14 14:41:01 -0800203 private Runnable mOnDismissRunnable;
Mady Mellorb0a82462016-04-30 17:31:02 -0700204
Mady Mellor43c2cd12016-12-12 21:05:13 -0800205 @Override
Mady Mellorb0a82462016-04-30 17:31:02 -0700206 public boolean isGroupExpansionChanging() {
207 if (isChildInGroup()) {
208 return mNotificationParent.isGroupExpansionChanging();
209 }
210 return mGroupExpansionChanging;
211 }
212
213 public void setGroupExpansionChanging(boolean changing) {
214 mGroupExpansionChanging = changing;
215 }
Jorim Jaggi5eb67c22015-08-19 19:50:49 -0700216
Adrian Roos599be342016-06-13 14:54:39 -0700217 @Override
218 public void setActualHeightAnimating(boolean animating) {
219 if (mPrivateLayout != null) {
220 mPrivateLayout.setContentHeightAnimating(animating);
221 }
222 }
223
Selim Cinek8d490d42015-04-10 00:05:50 -0700224 public NotificationContentView getPrivateLayout() {
225 return mPrivateLayout;
226 }
227
228 public NotificationContentView getPublicLayout() {
229 return mPublicLayout;
230 }
231
Selim Cinekcab4a602014-09-03 14:47:57 +0200232 public void setIconAnimationRunning(boolean running) {
Adrian Rooseb434ff2017-01-11 11:18:48 -0800233 for (NotificationContentView l : mLayouts) {
234 setIconAnimationRunning(running, l);
235 }
Selim Cinek5a175d92015-11-23 18:01:33 -0800236 if (mIsSummaryWithChildren) {
Mady Mellorb0a82462016-04-30 17:31:02 -0700237 setIconAnimationRunningForChild(running, mChildrenContainer.getHeaderView());
Selim Cinek5a175d92015-11-23 18:01:33 -0800238 List<ExpandableNotificationRow> notificationChildren =
239 mChildrenContainer.getNotificationChildren();
240 for (int i = 0; i < notificationChildren.size(); i++) {
241 ExpandableNotificationRow child = notificationChildren.get(i);
242 child.setIconAnimationRunning(running);
243 }
244 }
245 mIconAnimationRunning = running;
Selim Cinekcab4a602014-09-03 14:47:57 +0200246 }
247
248 private void setIconAnimationRunning(boolean running, NotificationContentView layout) {
249 if (layout != null) {
250 View contractedChild = layout.getContractedChild();
251 View expandedChild = layout.getExpandedChild();
Selim Cinek8d490d42015-04-10 00:05:50 -0700252 View headsUpChild = layout.getHeadsUpChild();
Selim Cinekcab4a602014-09-03 14:47:57 +0200253 setIconAnimationRunningForChild(running, contractedChild);
254 setIconAnimationRunningForChild(running, expandedChild);
Selim Cinek8d490d42015-04-10 00:05:50 -0700255 setIconAnimationRunningForChild(running, headsUpChild);
Selim Cinekcab4a602014-09-03 14:47:57 +0200256 }
257 }
258
259 private void setIconAnimationRunningForChild(boolean running, View child) {
260 if (child != null) {
261 ImageView icon = (ImageView) child.findViewById(com.android.internal.R.id.icon);
262 setIconRunning(icon, running);
263 ImageView rightIcon = (ImageView) child.findViewById(
264 com.android.internal.R.id.right_icon);
265 setIconRunning(rightIcon, running);
266 }
267 }
268
269 private void setIconRunning(ImageView imageView, boolean running) {
270 if (imageView != null) {
271 Drawable drawable = imageView.getDrawable();
272 if (drawable instanceof AnimationDrawable) {
273 AnimationDrawable animationDrawable = (AnimationDrawable) drawable;
274 if (running) {
275 animationDrawable.start();
276 } else {
277 animationDrawable.stop();
278 }
279 } else if (drawable instanceof AnimatedVectorDrawable) {
280 AnimatedVectorDrawable animationDrawable = (AnimatedVectorDrawable) drawable;
281 if (running) {
282 animationDrawable.start();
283 } else {
284 animationDrawable.stop();
285 }
286 }
287 }
288 }
289
Selim Cinekda42d652015-12-04 15:51:16 -0800290 public void onNotificationUpdated(NotificationData.Entry entry) {
291 mEntry = entry;
292 mStatusBarNotification = entry.notification;
Adrian Rooseb434ff2017-01-11 11:18:48 -0800293 for (NotificationContentView l : mLayouts) {
294 l.onNotificationUpdated(entry);
295 }
Selim Cinek757d8792016-01-28 16:21:08 -0800296 mShowingPublicInitialized = false;
Selim Cinek4bb59342016-04-08 19:29:35 -0700297 updateNotificationColor();
Selim Cinek8fc93c92015-11-23 17:48:07 -0800298 if (mIsSummaryWithChildren) {
Mady Mellorb0a82462016-04-30 17:31:02 -0700299 mChildrenContainer.recreateNotificationHeader(mExpandClickListener, mEntry.notification);
Selim Cinekc897bd32016-03-18 17:32:31 -0700300 mChildrenContainer.onNotificationUpdated();
Selim Cinek8fc93c92015-11-23 17:48:07 -0800301 }
Selim Cinek5a175d92015-11-23 18:01:33 -0800302 if (mIconAnimationRunning) {
303 setIconAnimationRunning(true);
304 }
Selim Cinekea4bef72015-12-02 15:51:10 -0800305 if (mNotificationParent != null) {
306 mNotificationParent.updateChildrenHeaderAppearance();
307 }
Selim Cinek263398f2015-10-21 17:40:23 -0700308 onChildrenCountChanged();
Selim Cinek624c02db2015-12-14 21:00:02 -0800309 // The public layouts expand button is always visible
310 mPublicLayout.updateExpandButtons(true);
Selim Cinekda42d652015-12-04 15:51:16 -0800311 updateLimits();
Selim Cinek0242fbb2016-10-19 13:38:32 -0700312 updateIconVisibilities();
Selim Cinekda42d652015-12-04 15:51:16 -0800313 }
314
315 private void updateLimits() {
Adrian Rooseb434ff2017-01-11 11:18:48 -0800316 for (NotificationContentView l : mLayouts) {
317 updateLimitsForView(l);
318 }
Selim Cineka1744872016-03-11 15:36:06 -0800319 }
320
321 private void updateLimitsForView(NotificationContentView layout) {
322 boolean customView = layout.getContractedChild().getId()
Selim Cinekda42d652015-12-04 15:51:16 -0800323 != com.android.internal.R.id.status_bar_latest_event_content;
324 boolean beforeN = mEntry.targetSdk < Build.VERSION_CODES.N;
325 int minHeight = customView && beforeN && !mIsSummaryWithChildren ?
326 mNotificationMinHeightLegacy : mNotificationMinHeight;
Selim Cineka1744872016-03-11 15:36:06 -0800327 boolean headsUpCustom = layout.getHeadsUpChild() != null &&
328 layout.getHeadsUpChild().getId()
329 != com.android.internal.R.id.status_bar_latest_event_content;
Selim Cinek77019c72015-12-09 10:18:02 -0800330 int headsUpheight = headsUpCustom && beforeN ? mMaxHeadsUpHeightLegacy
331 : mMaxHeadsUpHeight;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800332 layout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight,
333 mNotificationAmbientHeight);
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200334 }
335
336 public StatusBarNotification getStatusBarNotification() {
337 return mStatusBarNotification;
338 }
339
Selim Cinek281c2022016-10-13 19:14:43 -0700340 public NotificationData.Entry getEntry() {
341 return mEntry;
342 }
343
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700344 public boolean isHeadsUp() {
345 return mIsHeadsUp;
346 }
347
Selim Cinek1a521f32014-11-03 17:39:29 +0100348 public void setHeadsUp(boolean isHeadsUp) {
Selim Cinekc80fdb12015-04-13 15:09:08 -0700349 int intrinsicBefore = getIntrinsicHeight();
Selim Cinek1a521f32014-11-03 17:39:29 +0100350 mIsHeadsUp = isHeadsUp;
Selim Cinek8d490d42015-04-10 00:05:50 -0700351 mPrivateLayout.setHeadsUp(isHeadsUp);
Selim Cinekb41b2f62016-04-26 14:03:29 -0700352 if (mIsSummaryWithChildren) {
353 // The overflow might change since we allow more lines as HUN.
354 mChildrenContainer.updateGroupOverflow();
355 }
Selim Cinekc80fdb12015-04-13 15:09:08 -0700356 if (intrinsicBefore != getIntrinsicHeight()) {
357 notifyHeightChanged(false /* needsAnimation */);
358 }
Selim Cinekd127d792016-11-01 19:11:41 -0700359 if (isHeadsUp) {
360 setAboveShelf(true);
361 }
Selim Cinek1a521f32014-11-03 17:39:29 +0100362 }
363
Selim Cinekb5605e52015-02-20 18:21:41 +0100364 public void setGroupManager(NotificationGroupManager groupManager) {
365 mGroupManager = groupManager;
Selim Cinek83bc7832015-10-22 13:26:54 -0700366 mPrivateLayout.setGroupManager(groupManager);
Selim Cinekb5605e52015-02-20 18:21:41 +0100367 }
368
Adrian Roosb88b1a12015-12-09 18:51:05 -0800369 public void setRemoteInputController(RemoteInputController r) {
370 mPrivateLayout.setRemoteInputController(r);
371 }
372
Mady Mellor3fd273e2016-03-15 21:08:14 -0700373 public void setAppName(String appName) {
374 mAppName = appName;
Mady Mellor761cde12017-01-10 11:36:39 -0800375 if (mMenuRow != null) {
376 mMenuRow.setAppName(mAppName);
Mady Mellor3fd273e2016-03-15 21:08:14 -0700377 }
378 }
379
Selim Cinekb5605e52015-02-20 18:21:41 +0100380 public void addChildNotification(ExpandableNotificationRow row) {
381 addChildNotification(row, -1);
382 }
383
384 /**
385 * Add a child notification to this view.
386 *
387 * @param row the row to add
388 * @param childIndex the index to add it at, if -1 it will be added at the end
389 */
390 public void addChildNotification(ExpandableNotificationRow row, int childIndex) {
391 if (mChildrenContainer == null) {
392 mChildrenContainerStub.inflate();
393 }
394 mChildrenContainer.addNotification(row, childIndex);
Selim Cinek263398f2015-10-21 17:40:23 -0700395 onChildrenCountChanged();
396 row.setIsChildInGroup(true, this);
Selim Cinekb5605e52015-02-20 18:21:41 +0100397 }
398
399 public void removeChildNotification(ExpandableNotificationRow row) {
400 if (mChildrenContainer != null) {
401 mChildrenContainer.removeNotification(row);
402 }
Selim Cinek263398f2015-10-21 17:40:23 -0700403 onChildrenCountChanged();
404 row.setIsChildInGroup(false, null);
405 }
406
Mady Mellor43c2cd12016-12-12 21:05:13 -0800407 @Override
Selim Cinek263398f2015-10-21 17:40:23 -0700408 public boolean isChildInGroup() {
Selim Cineka6c6bfb2015-10-29 16:27:08 -0700409 return mNotificationParent != null;
Selim Cinek263398f2015-10-21 17:40:23 -0700410 }
411
Selim Cinek388df6d2015-10-22 13:25:11 -0700412 public ExpandableNotificationRow getNotificationParent() {
413 return mNotificationParent;
414 }
415
Selim Cinek263398f2015-10-21 17:40:23 -0700416 /**
417 * @param isChildInGroup Is this notification now in a group
418 * @param parent the new parent notification
419 */
Selim Cineka6c6bfb2015-10-29 16:27:08 -0700420 public void setIsChildInGroup(boolean isChildInGroup, ExpandableNotificationRow parent) {;
421 boolean childInGroup = BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS && isChildInGroup;
422 mNotificationParent = childInGroup ? parent : null;
423 mPrivateLayout.setIsChildInGroup(childInGroup);
Mady Mellorc7d65b42016-05-04 11:44:57 -0400424 resetBackgroundAlpha();
Mady Mellorb0a82462016-04-30 17:31:02 -0700425 updateBackgroundForGroupState();
Selim Cinekddf1b392016-05-27 16:33:10 -0700426 updateClickAndFocus();
Mady Mellorb0a82462016-04-30 17:31:02 -0700427 if (mNotificationParent != null) {
428 mNotificationParent.updateBackgroundForGroupState();
429 }
Selim Cinekdb167372016-11-17 15:41:17 -0800430 updateIconVisibilities();
Selim Cinek34d93b02015-10-22 12:30:38 -0700431 }
432
433 @Override
Selim Cinek72109472016-01-15 16:33:22 -0800434 public boolean onTouchEvent(MotionEvent event) {
435 if (event.getActionMasked() != MotionEvent.ACTION_DOWN
436 || !isChildInGroup() || isGroupExpanded()) {
437 return super.onTouchEvent(event);
438 } else {
439 return false;
440 }
441 }
442
443 @Override
Mady Mellorf0625802016-02-11 18:03:48 -0800444 protected boolean handleSlideBack() {
Mady Mellor761cde12017-01-10 11:36:39 -0800445 if (mMenuRow != null && mMenuRow.isVisible()) {
Mady Mellorf0625802016-02-11 18:03:48 -0800446 animateTranslateNotification(0 /* targetLeft */);
447 return true;
448 }
449 return false;
450 }
451
452 @Override
Selim Cinek34d93b02015-10-22 12:30:38 -0700453 protected boolean shouldHideBackground() {
454 return super.shouldHideBackground() || mShowNoBackground;
Selim Cinek263398f2015-10-21 17:40:23 -0700455 }
456
457 @Override
458 public boolean isSummaryWithChildren() {
459 return mIsSummaryWithChildren;
Selim Cinekb5605e52015-02-20 18:21:41 +0100460 }
461
462 @Override
463 public boolean areChildrenExpanded() {
464 return mChildrenExpanded;
465 }
466
467 public List<ExpandableNotificationRow> getNotificationChildren() {
468 return mChildrenContainer == null ? null : mChildrenContainer.getNotificationChildren();
469 }
470
Selim Cinekeef84282015-10-30 16:28:00 -0700471 public int getNumberOfNotificationChildren() {
472 if (mChildrenContainer == null) {
473 return 0;
474 }
475 return mChildrenContainer.getNotificationChildren().size();
476 }
477
Selim Cinekb5605e52015-02-20 18:21:41 +0100478 /**
479 * Apply the order given in the list to the children.
480 *
481 * @param childOrder the new list order
Selim Cineka7d4f822016-12-06 14:34:47 -0800482 * @param visualStabilityManager
483 * @param callback the callback to invoked in case it is not allowed
Selim Cinekb5605e52015-02-20 18:21:41 +0100484 * @return whether the list order has changed
485 */
Selim Cineka7d4f822016-12-06 14:34:47 -0800486 public boolean applyChildOrder(List<ExpandableNotificationRow> childOrder,
487 VisualStabilityManager visualStabilityManager,
488 VisualStabilityManager.Callback callback) {
489 return mChildrenContainer != null && mChildrenContainer.applyChildOrder(childOrder,
490 visualStabilityManager, callback);
Selim Cinekb5605e52015-02-20 18:21:41 +0100491 }
492
493 public void getChildrenStates(StackScrollState resultState) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700494 if (mIsSummaryWithChildren) {
Selim Cinekbbcebde2016-11-09 18:28:20 -0800495 ExpandableViewState parentState = resultState.getViewStateForView(this);
Selim Cinekb5605e52015-02-20 18:21:41 +0100496 mChildrenContainer.getState(resultState, parentState);
497 }
498 }
499
500 public void applyChildrenState(StackScrollState state) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700501 if (mIsSummaryWithChildren) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100502 mChildrenContainer.applyState(state);
503 }
504 }
505
506 public void prepareExpansionChanged(StackScrollState state) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700507 if (mIsSummaryWithChildren) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100508 mChildrenContainer.prepareExpansionChanged(state);
509 }
510 }
511
Selim Cinek0cfbef42016-11-09 19:06:36 -0800512 public void startChildAnimation(StackScrollState finalState, AnimationProperties properties) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700513 if (mIsSummaryWithChildren) {
Selim Cinek0cfbef42016-11-09 19:06:36 -0800514 mChildrenContainer.startAnimationToState(finalState, properties);
Selim Cinekb5605e52015-02-20 18:21:41 +0100515 }
516 }
517
518 public ExpandableNotificationRow getViewAtPosition(float y) {
Selim Cinek43d30f02016-03-04 10:51:32 -0800519 if (!mIsSummaryWithChildren || !mChildrenExpanded) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100520 return this;
521 } else {
522 ExpandableNotificationRow view = mChildrenContainer.getViewAtPosition(y);
523 return view == null ? this : view;
524 }
525 }
526
Selim Cinekab29aeb2015-02-20 18:18:32 +0100527 public NotificationGuts getGuts() {
528 return mGuts;
529 }
530
Selim Cinek684a4422015-04-15 16:18:39 -0700531 /**
532 * Set this notification to be pinned to the top if {@link #isHeadsUp()} is true. By doing this
533 * the notification will be rendered on top of the screen.
534 *
535 * @param pinned whether it is pinned
536 */
537 public void setPinned(boolean pinned) {
Selim Cinekdef35a82016-05-03 15:52:51 -0700538 int intrinsicHeight = getIntrinsicHeight();
Selim Cinek684a4422015-04-15 16:18:39 -0700539 mIsPinned = pinned;
Selim Cinekdef35a82016-05-03 15:52:51 -0700540 if (intrinsicHeight != getIntrinsicHeight()) {
Selim Cinekbb42b7d2016-08-10 13:02:38 -0700541 notifyHeightChanged(false /* needsAnimation */);
Selim Cinekdef35a82016-05-03 15:52:51 -0700542 }
Selim Cinek31aada42015-12-18 17:51:15 -0800543 if (pinned) {
544 setIconAnimationRunning(true);
545 mExpandedWhenPinned = false;
546 } else if (mExpandedWhenPinned) {
547 setUserExpanded(true);
548 }
Selim Cinek98713a42015-09-21 15:47:20 +0200549 setChronometerRunning(mLastChronometerRunning);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700550 }
551
Selim Cinek684a4422015-04-15 16:18:39 -0700552 public boolean isPinned() {
553 return mIsPinned;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700554 }
555
Selim Cinekd127d792016-11-01 19:11:41 -0700556 @Override
557 public int getPinnedHeadsUpHeight() {
558 return getPinnedHeadsUpHeight(true /* atLeastMinHeight */);
559 }
560
Selim Cinek31aada42015-12-18 17:51:15 -0800561 /**
562 * @param atLeastMinHeight should the value returned be at least the minimum height.
563 * Used to avoid cyclic calls
564 * @return the height of the heads up notification when pinned
565 */
Selim Cinekd127d792016-11-01 19:11:41 -0700566 private int getPinnedHeadsUpHeight(boolean atLeastMinHeight) {
Selim Cinek77019c72015-12-09 10:18:02 -0800567 if (mIsSummaryWithChildren) {
568 return mChildrenContainer.getIntrinsicHeight();
569 }
Selim Cinek31aada42015-12-18 17:51:15 -0800570 if(mExpandedWhenPinned) {
571 return Math.max(getMaxExpandHeight(), mHeadsUpHeight);
572 } else if (atLeastMinHeight) {
Selim Cinek567e8452016-03-24 10:54:56 -0700573 return Math.max(getCollapsedHeight(), mHeadsUpHeight);
Selim Cinek31aada42015-12-18 17:51:15 -0800574 } else {
575 return mHeadsUpHeight;
576 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700577 }
578
Jorim Jaggi5eb67c22015-08-19 19:50:49 -0700579 /**
580 * Mark whether this notification was just clicked, i.e. the user has just clicked this
581 * notification in this frame.
582 */
583 public void setJustClicked(boolean justClicked) {
584 mJustClicked = justClicked;
585 }
586
587 /**
588 * @return true if this notification has been clicked in this frame, false otherwise
589 */
590 public boolean wasJustClicked() {
591 return mJustClicked;
592 }
593
Selim Cinek98713a42015-09-21 15:47:20 +0200594 public void setChronometerRunning(boolean running) {
595 mLastChronometerRunning = running;
596 setChronometerRunning(running, mPrivateLayout);
597 setChronometerRunning(running, mPublicLayout);
598 if (mChildrenContainer != null) {
599 List<ExpandableNotificationRow> notificationChildren =
600 mChildrenContainer.getNotificationChildren();
601 for (int i = 0; i < notificationChildren.size(); i++) {
602 ExpandableNotificationRow child = notificationChildren.get(i);
603 child.setChronometerRunning(running);
604 }
605 }
606 }
607
608 private void setChronometerRunning(boolean running, NotificationContentView layout) {
609 if (layout != null) {
610 running = running || isPinned();
611 View contractedChild = layout.getContractedChild();
612 View expandedChild = layout.getExpandedChild();
613 View headsUpChild = layout.getHeadsUpChild();
614 setChronometerRunningForChild(running, contractedChild);
615 setChronometerRunningForChild(running, expandedChild);
616 setChronometerRunningForChild(running, headsUpChild);
617 }
618 }
619
620 private void setChronometerRunningForChild(boolean running, View child) {
621 if (child != null) {
622 View chronometer = child.findViewById(com.android.internal.R.id.chronometer);
623 if (chronometer instanceof Chronometer) {
624 ((Chronometer) chronometer).setStarted(running);
625 }
626 }
627 }
628
Selim Cinekea4bef72015-12-02 15:51:10 -0800629 public NotificationHeaderView getNotificationHeader() {
Mady Mellorb0a82462016-04-30 17:31:02 -0700630 if (mIsSummaryWithChildren) {
631 return mChildrenContainer.getHeaderView();
Selim Cinek8d6440d2015-10-22 13:00:05 -0700632 }
Selim Cinekea4bef72015-12-02 15:51:10 -0800633 return mPrivateLayout.getNotificationHeader();
Selim Cinek8d6440d2015-10-22 13:00:05 -0700634 }
635
Selim Cinek34eda5e2016-02-18 17:10:43 -0800636 private NotificationHeaderView getVisibleNotificationHeader() {
Selim Cinekaa3901a2016-08-05 11:04:37 -0700637 if (mIsSummaryWithChildren && !mShowingPublic) {
Mady Mellorb0a82462016-04-30 17:31:02 -0700638 return mChildrenContainer.getHeaderView();
Selim Cinek34eda5e2016-02-18 17:10:43 -0800639 }
640 return getShowingLayout().getVisibleNotificationHeader();
641 }
642
Selim Cinek570981d2015-12-01 11:37:01 -0800643 public void setOnExpandClickListener(OnExpandClickListener onExpandClickListener) {
644 mOnExpandClickListener = onExpandClickListener;
645 }
646
Selim Cinekddf1b392016-05-27 16:33:10 -0700647 @Override
648 public void setOnClickListener(@Nullable OnClickListener l) {
649 super.setOnClickListener(l);
650 mOnClickListener = l;
651 updateClickAndFocus();
652 }
653
654 private void updateClickAndFocus() {
655 boolean normalChild = !isChildInGroup() || isGroupExpanded();
656 boolean clickable = mOnClickListener != null && normalChild;
657 if (isFocusable() != normalChild) {
658 setFocusable(normalChild);
659 }
660 if (isClickable() != clickable) {
661 setClickable(clickable);
662 }
663 }
664
Selim Cinek31aada42015-12-18 17:51:15 -0800665 public void setHeadsUpManager(HeadsUpManager headsUpManager) {
666 mHeadsUpManager = headsUpManager;
667 }
668
Selim Cinek01af3342016-02-09 19:25:31 -0800669 public void reInflateViews() {
670 initDimens();
671 if (mIsSummaryWithChildren) {
Selim Cinek01af3342016-02-09 19:25:31 -0800672 if (mChildrenContainer != null) {
Mady Mellorb0a82462016-04-30 17:31:02 -0700673 mChildrenContainer.reInflateViews(mExpandClickListener, mEntry.notification);
Selim Cinek01af3342016-02-09 19:25:31 -0800674 }
675 }
676 if (mGuts != null) {
677 View oldGuts = mGuts;
678 int index = indexOfChild(oldGuts);
679 removeView(oldGuts);
680 mGuts = (NotificationGuts) LayoutInflater.from(mContext).inflate(
681 R.layout.notification_guts, this, false);
682 mGuts.setVisibility(oldGuts.getVisibility());
683 addView(mGuts, index);
684 }
Mady Mellor761cde12017-01-10 11:36:39 -0800685 if (mMenuRow != null) {
686 View oldMenu = mMenuRow;
687 int menuIndex = indexOfChild(oldMenu);
688 removeView(oldMenu);
689 mMenuRow = (NotificationMenuRow) LayoutInflater.from(mContext).inflate(
690 R.layout.notification_menu_row, this, false);
691 mMenuRow.setNotificationRowParent(ExpandableNotificationRow.this);
692 mMenuRow.setAppName(mAppName);
693 mMenuRow.setVisibility(oldMenu.getVisibility());
694 addView(mMenuRow, menuIndex);
Mady Mellor4b80b102016-01-22 08:03:58 -0800695
696 }
Adrian Rooseb434ff2017-01-11 11:18:48 -0800697 for (NotificationContentView l : mLayouts) {
698 l.reInflateViews();
699 }
Selim Cinek01af3342016-02-09 19:25:31 -0800700 }
701
Selim Cinekc3179332016-03-04 14:44:56 -0800702 public void setContentBackground(int customBackgroundColor, boolean animate,
703 NotificationContentView notificationContentView) {
704 if (getShowingLayout() == notificationContentView) {
705 setTintColor(customBackgroundColor, animate);
706 }
707 }
708
Adrian Roos0bd8a4b2016-03-14 16:21:44 -0700709 public void closeRemoteInput() {
Adrian Rooseb434ff2017-01-11 11:18:48 -0800710 for (NotificationContentView l : mLayouts) {
711 l.closeRemoteInput();
712 }
Adrian Roos0bd8a4b2016-03-14 16:21:44 -0700713 }
714
Selim Cinekc897bd32016-03-18 17:32:31 -0700715 /**
716 * Set by how much the single line view should be indented.
717 */
718 public void setSingleLineWidthIndention(int indention) {
719 mPrivateLayout.setSingleLineWidthIndention(indention);
720 }
721
722 public int getNotificationColor() {
Selim Cinek4bb59342016-04-08 19:29:35 -0700723 return mNotificationColor;
724 }
725
726 private void updateNotificationColor() {
727 mNotificationColor = NotificationColorUtil.resolveContrastColor(mContext,
728 getStatusBarNotification().getNotification().color);
Selim Cinekc897bd32016-03-18 17:32:31 -0700729 }
730
731 public HybridNotificationView getSingleLineView() {
732 return mPrivateLayout.getSingleLineView();
733 }
734
Selim Cinekf07d0622016-03-21 19:52:52 -0700735 public boolean isOnKeyguard() {
736 return mOnKeyguard;
737 }
738
Selim Cinekc1e389d2016-04-07 11:02:57 -0700739 public void removeAllChildren() {
740 List<ExpandableNotificationRow> notificationChildren
741 = mChildrenContainer.getNotificationChildren();
742 ArrayList<ExpandableNotificationRow> clonedList = new ArrayList<>(notificationChildren);
743 for (int i = 0; i < clonedList.size(); i++) {
744 ExpandableNotificationRow row = clonedList.get(i);
Selim Cinek3f19f602016-05-02 18:01:56 -0700745 if (row.keepInParent()) {
746 continue;
747 }
Selim Cinekc1e389d2016-04-07 11:02:57 -0700748 mChildrenContainer.removeNotification(row);
Selim Cinekc1e389d2016-04-07 11:02:57 -0700749 row.setIsChildInGroup(false, null);
750 }
751 onChildrenCountChanged();
752 }
753
Selim Cinek1b2a05e2016-04-28 14:20:39 -0700754 public void setForceUnlocked(boolean forceUnlocked) {
755 mForceUnlocked = forceUnlocked;
756 if (mIsSummaryWithChildren) {
757 List<ExpandableNotificationRow> notificationChildren = getNotificationChildren();
758 for (ExpandableNotificationRow child : notificationChildren) {
759 child.setForceUnlocked(forceUnlocked);
760 }
761 }
762 }
763
Selim Cineke9bad242016-06-15 11:46:37 -0700764 public void setDismissed(boolean dismissed, boolean fromAccessibility) {
Selim Cinek3f19f602016-05-02 18:01:56 -0700765 mDismissed = dismissed;
Selim Cineke9bad242016-06-15 11:46:37 -0700766 mGroupParentWhenDismissed = mNotificationParent;
767 mRefocusOnDismiss = fromAccessibility;
768 mChildAfterViewWhenDismissed = null;
769 if (isChildInGroup()) {
770 List<ExpandableNotificationRow> notificationChildren =
771 mNotificationParent.getNotificationChildren();
772 int i = notificationChildren.indexOf(this);
773 if (i != -1 && i < notificationChildren.size() - 1) {
774 mChildAfterViewWhenDismissed = notificationChildren.get(i + 1);
775 }
776 }
Selim Cinek3f19f602016-05-02 18:01:56 -0700777 }
778
779 public boolean isDismissed() {
780 return mDismissed;
781 }
782
783 public boolean keepInParent() {
784 return mKeepInParent;
785 }
786
787 public void setKeepInParent(boolean keepInParent) {
788 mKeepInParent = keepInParent;
789 }
790
791 public boolean isRemoved() {
792 return mRemoved;
793 }
794
Adrian Roosd009ab12016-05-20 17:58:53 -0700795 public void setRemoved() {
796 mRemoved = true;
797
798 mPrivateLayout.setRemoved();
Selim Cinek3f19f602016-05-02 18:01:56 -0700799 }
800
Selim Cinekd1395642016-04-28 12:22:42 -0700801 public NotificationChildrenContainer getChildrenContainer() {
802 return mChildrenContainer;
803 }
804
Selim Cinekcafa87f2016-10-26 17:00:17 -0700805 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
806 mHeadsupDisappearRunning = headsUpAnimatingAway;
807 mPrivateLayout.setHeadsUpAnimatingAway(headsUpAnimatingAway);
808 }
809
810 /**
811 * @return if the view was just heads upped and is now animating away. During such a time the
812 * layout needs to be kept consistent
813 */
814 public boolean isHeadsUpAnimatingAway() {
815 return mHeadsupDisappearRunning;
Selim Cinek73cf02a2016-06-17 13:08:00 -0700816 }
817
Selim Cineke9bad242016-06-15 11:46:37 -0700818 public View getChildAfterViewWhenDismissed() {
819 return mChildAfterViewWhenDismissed;
820 }
821
822 public View getGroupParentWhenDismissed() {
823 return mGroupParentWhenDismissed;
824 }
825
Selim Cinek9e624e72016-07-20 13:46:49 -0700826 public void performDismiss() {
Selim Cineke9079112016-12-14 14:41:01 -0800827 if (mOnDismissRunnable != null) {
828 mOnDismissRunnable.run();
829 }
Selim Cinek9e624e72016-07-20 13:46:49 -0700830 }
831
Selim Cineke9079112016-12-14 14:41:01 -0800832 public void setOnDismissRunnable(Runnable onDismissRunnable) {
833 mOnDismissRunnable = onDismissRunnable;
Selim Cinek9e624e72016-07-20 13:46:49 -0700834 }
835
Selim Cinek281c2022016-10-13 19:14:43 -0700836 public View getNotificationIcon() {
837 NotificationHeaderView notificationHeader = getNotificationHeader();
838 if (notificationHeader != null) {
839 return notificationHeader.getIcon();
840 }
841 return null;
842 }
843
844 /**
845 * @return whether the notification is currently showing a view with an icon.
846 */
847 public boolean isShowingIcon() {
848 if (mIsSummaryWithChildren) {
849 return true;
850 }
851 NotificationContentView showingLayout = getShowingLayout();
852 NotificationHeaderView notificationHeader = showingLayout.getVisibleNotificationHeader();
853 return notificationHeader != null;
854 }
855
Selim Cinek0242fbb2016-10-19 13:38:32 -0700856 /**
857 * Set how much this notification is transformed into an icon.
858 *
Selim Cinek2b549f42016-11-22 16:38:51 -0800859 * @param contentTransformationAmount A value from 0 to 1 indicating how much we are transformed
860 * to the content away
Selim Cinek875a3a12016-11-18 17:52:16 -0800861 * @param isLastChild is this the last child in the list. If true, then the transformation is
862 * different since it's content fades out.
Selim Cinek0242fbb2016-10-19 13:38:32 -0700863 */
Selim Cinek2b549f42016-11-22 16:38:51 -0800864 public void setContentTransformationAmount(float contentTransformationAmount,
865 boolean isLastChild) {
Selim Cinek875a3a12016-11-18 17:52:16 -0800866 boolean changeTransformation = isLastChild != mIsLastChild;
Selim Cinek2b549f42016-11-22 16:38:51 -0800867 changeTransformation |= mContentTransformationAmount != contentTransformationAmount;
Selim Cinek875a3a12016-11-18 17:52:16 -0800868 mIsLastChild = isLastChild;
Selim Cinek2b549f42016-11-22 16:38:51 -0800869 mContentTransformationAmount = contentTransformationAmount;
Selim Cinek875a3a12016-11-18 17:52:16 -0800870 if (changeTransformation) {
871 updateContentTransformation();
Selim Cinek2b549f42016-11-22 16:38:51 -0800872 }
873 }
874
875 /**
876 * Set the icons to be visible of this notification.
877 */
878 public void setIconsVisible(boolean iconsVisible) {
879 if (iconsVisible != mIconsVisible) {
880 mIconsVisible = iconsVisible;
881 updateIconVisibilities();
Selim Cinek0242fbb2016-10-19 13:38:32 -0700882 }
883 }
884
Selim Cinekdb167372016-11-17 15:41:17 -0800885 @Override
886 protected void onBelowSpeedBumpChanged() {
887 updateIconVisibilities();
888 }
889
Selim Cinek875a3a12016-11-18 17:52:16 -0800890 private void updateContentTransformation() {
891 float contentAlpha;
Selim Cinek2b549f42016-11-22 16:38:51 -0800892 float translationY = -mContentTransformationAmount * mIconTransformContentShift;
Selim Cinek875a3a12016-11-18 17:52:16 -0800893 if (mIsLastChild) {
Selim Cinek2b549f42016-11-22 16:38:51 -0800894 contentAlpha = 1.0f - mContentTransformationAmount;
Selim Cinek875a3a12016-11-18 17:52:16 -0800895 contentAlpha = Math.min(contentAlpha / 0.5f, 1.0f);
Selim Cinek0242fbb2016-10-19 13:38:32 -0700896 contentAlpha = Interpolators.ALPHA_OUT.getInterpolation(contentAlpha);
Selim Cinek875a3a12016-11-18 17:52:16 -0800897 translationY *= 0.4f;
898 } else {
899 contentAlpha = 1.0f;
900 }
Adrian Rooseb434ff2017-01-11 11:18:48 -0800901 for (NotificationContentView l : mLayouts) {
902 l.setAlpha(contentAlpha);
903 l.setTranslationY(translationY);
904 }
Selim Cinek875a3a12016-11-18 17:52:16 -0800905 if (mChildrenContainer != null) {
906 mChildrenContainer.setAlpha(contentAlpha);
907 mChildrenContainer.setTranslationY(translationY);
908 // TODO: handle children fade out better
Selim Cinek0242fbb2016-10-19 13:38:32 -0700909 }
910 }
911
912 private void updateIconVisibilities() {
Selim Cinek17e1b692016-12-02 18:19:11 -0800913 boolean visible = isChildInGroup()
914 || (isBelowSpeedBump() && !NotificationShelf.SHOW_AMBIENT_ICONS)
915 || mIconsVisible;
Adrian Rooseb434ff2017-01-11 11:18:48 -0800916 for (NotificationContentView l : mLayouts) {
917 l.setIconsVisible(visible);
918 }
Selim Cinekdb167372016-11-17 15:41:17 -0800919 if (mChildrenContainer != null) {
920 mChildrenContainer.setIconsVisible(visible);
Selim Cinek0242fbb2016-10-19 13:38:32 -0700921 }
922 }
923
Selim Cinek875a3a12016-11-18 17:52:16 -0800924 /**
925 * Get the relative top padding of a view relative to this view. This recursively walks up the
926 * hierarchy and does the corresponding measuring.
927 *
928 * @param view the view to the the padding for. The requested view has to be a child of this
929 * notification.
930 * @return the toppadding
931 */
932 public int getRelativeTopPadding(View view) {
933 int topPadding = 0;
934 while (view.getParent() instanceof ViewGroup) {
935 topPadding += view.getTop();
936 view = (View) view.getParent();
937 if (view instanceof ExpandableNotificationRow) {
938 return topPadding;
939 }
940 }
941 return topPadding;
942 }
943
Selim Cineka1d97902016-12-14 16:31:40 -0800944 public float getContentTranslation() {
945 return mPrivateLayout.getTranslationY();
946 }
947
Chris Wren78403d72014-07-28 10:23:24 +0100948 public interface ExpansionLogger {
949 public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
950 }
Selim Cinek1685e632014-04-08 02:27:49 +0200951
Chris Wren51c75102013-07-16 20:49:17 -0400952 public ExpandableNotificationRow(Context context, AttributeSet attrs) {
953 super(context, attrs);
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700954 mFalsingManager = FalsingManager.getInstance(context);
Selim Cinek01af3342016-02-09 19:25:31 -0800955 initDimens();
956 }
957
958 private void initDimens() {
Selim Cinekf619ffc2016-02-17 14:53:05 -0800959 mNotificationMinHeightLegacy = getFontScaledHeight(R.dimen.notification_min_height_legacy);
960 mNotificationMinHeight = getFontScaledHeight(R.dimen.notification_min_height);
961 mNotificationMaxHeight = getFontScaledHeight(R.dimen.notification_max_height);
Adrian Roos0aac04f2016-12-08 15:59:29 -0800962 mNotificationAmbientHeight = getFontScaledHeight(R.dimen.notification_ambient_height);
Selim Cinekf619ffc2016-02-17 14:53:05 -0800963 mMaxHeadsUpHeightLegacy = getFontScaledHeight(
Selim Cinek77019c72015-12-09 10:18:02 -0800964 R.dimen.notification_max_heads_up_height_legacy);
Selim Cinekf619ffc2016-02-17 14:53:05 -0800965 mMaxHeadsUpHeight = getFontScaledHeight(R.dimen.notification_max_heads_up_height);
Mady Mellorb0a82462016-04-30 17:31:02 -0700966 mIncreasedPaddingBetweenElements = getResources()
967 .getDimensionPixelSize(R.dimen.notification_divider_height_increased);
Selim Cinek875a3a12016-11-18 17:52:16 -0800968 mIconTransformContentShiftNoIcon = getResources().getDimensionPixelSize(
Selim Cinek0242fbb2016-10-19 13:38:32 -0700969 R.dimen.notification_icon_transform_content_shift);
Selim Cinekf619ffc2016-02-17 14:53:05 -0800970 }
971
972 /**
973 * @param dimenId the dimen to look up
974 * @return the font scaled dimen as if it were in sp but doesn't shrink sizes below dp
975 */
976 private int getFontScaledHeight(int dimenId) {
977 int dimensionPixelSize = getResources().getDimensionPixelSize(dimenId);
978 float factor = Math.max(1.0f, getResources().getDisplayMetrics().scaledDensity /
979 getResources().getDisplayMetrics().density);
980 return (int) (dimensionPixelSize * factor);
Chris Wren51c75102013-07-16 20:49:17 -0400981 }
982
Christoph Studera7fe6312014-06-27 19:32:44 +0200983 /**
984 * Resets this view so it can be re-used for an updated notification.
985 */
Christoph Studer22f2ee52014-07-29 22:57:21 +0200986 @Override
Christoph Studera7fe6312014-06-27 19:32:44 +0200987 public void reset() {
Christoph Studer22f2ee52014-07-29 22:57:21 +0200988 super.reset();
Chris Wren78403d72014-07-28 10:23:24 +0100989 final boolean wasExpanded = isExpanded();
Christoph Studera7fe6312014-06-27 19:32:44 +0200990 mExpandable = false;
991 mHasUserChangedExpansion = false;
992 mUserLocked = false;
993 mShowingPublic = false;
Jorim Jaggiae441282014-08-01 02:45:18 +0200994 mSensitive = false;
995 mShowingPublicInitialized = false;
Christoph Studera7fe6312014-06-27 19:32:44 +0200996 mIsSystemExpanded = false;
Selim Cinek83bc7832015-10-22 13:26:54 -0700997 mOnKeyguard = false;
Selim Cinek51d94912016-03-02 15:34:28 -0800998 mPublicLayout.reset();
999 mPrivateLayout.reset();
Selim Cinek31094df2014-08-14 19:28:15 +02001000 resetHeight();
Mady Mellor4b80b102016-01-22 08:03:58 -08001001 resetTranslation();
Selim Cinek31094df2014-08-14 19:28:15 +02001002 logExpansionEvent(false, wasExpanded);
1003 }
1004
1005 public void resetHeight() {
Selim Cinek31094df2014-08-14 19:28:15 +02001006 onHeightReset();
Selim Cinek6e28a672014-09-05 14:43:28 +02001007 requestLayout();
Christoph Studera7fe6312014-06-27 19:32:44 +02001008 }
1009
Jorim Jaggi251957d2014-04-09 04:24:09 +02001010 @Override
1011 protected void onFinishInflate() {
1012 super.onFinishInflate();
Jorim Jaggibe565df2014-04-28 17:51:23 +02001013 mPublicLayout = (NotificationContentView) findViewById(R.id.expandedPublic);
1014 mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded);
Adrian Rooseb434ff2017-01-11 11:18:48 -08001015 mLayouts = new NotificationContentView[] {mPrivateLayout, mPublicLayout};
1016
1017 for (NotificationContentView l : mLayouts) {
1018 l.setExpandClickListener(mExpandClickListener);
1019 l.setContainingNotification(this);
1020 }
Mady Mellor761cde12017-01-10 11:36:39 -08001021 mMenuRowStub = (ViewStub) findViewById(R.id.menu_row_stub);
1022 mMenuRowStub.setOnInflateListener(new ViewStub.OnInflateListener() {
Mady Mellor4b80b102016-01-22 08:03:58 -08001023 @Override
1024 public void onInflate(ViewStub stub, View inflated) {
Mady Mellor761cde12017-01-10 11:36:39 -08001025 mMenuRow = (NotificationMenuRow) inflated;
1026 mMenuRow.setNotificationRowParent(ExpandableNotificationRow.this);
1027 mMenuRow.setAppName(mAppName);
Mady Mellor4b80b102016-01-22 08:03:58 -08001028 }
1029 });
Selim Cinekab29aeb2015-02-20 18:18:32 +01001030 mGutsStub = (ViewStub) findViewById(R.id.notification_guts_stub);
1031 mGutsStub.setOnInflateListener(new ViewStub.OnInflateListener() {
Jorim Jaggib1cd3c12014-09-08 19:55:17 +02001032 @Override
1033 public void onInflate(ViewStub stub, View inflated) {
1034 mGuts = (NotificationGuts) inflated;
1035 mGuts.setClipTopAmount(getClipTopAmount());
1036 mGuts.setActualHeight(getActualHeight());
Selim Cinekab29aeb2015-02-20 18:18:32 +01001037 mGutsStub = null;
Jorim Jaggib1cd3c12014-09-08 19:55:17 +02001038 }
1039 });
Selim Cinekb5605e52015-02-20 18:21:41 +01001040 mChildrenContainerStub = (ViewStub) findViewById(R.id.child_container_stub);
1041 mChildrenContainerStub.setOnInflateListener(new ViewStub.OnInflateListener() {
1042
1043 @Override
1044 public void onInflate(ViewStub stub, View inflated) {
1045 mChildrenContainer = (NotificationChildrenContainer) inflated;
Selim Cinek388df6d2015-10-22 13:25:11 -07001046 mChildrenContainer.setNotificationParent(ExpandableNotificationRow.this);
Selim Cinekc897bd32016-03-18 17:32:31 -07001047 mChildrenContainer.onNotificationUpdated();
Mady Mellor4b80b102016-01-22 08:03:58 -08001048 mTranslateableViews.add(mChildrenContainer);
Selim Cinekb5605e52015-02-20 18:21:41 +01001049 }
1050 });
Mady Mellor4b80b102016-01-22 08:03:58 -08001051
1052 // Add the views that we translate to reveal the gear
1053 mTranslateableViews = new ArrayList<View>();
1054 for (int i = 0; i < getChildCount(); i++) {
1055 mTranslateableViews.add(getChildAt(i));
1056 }
1057 // Remove views that don't translate
Mady Mellor761cde12017-01-10 11:36:39 -08001058 mTranslateableViews.remove(mMenuRowStub);
Mady Mellor4b80b102016-01-22 08:03:58 -08001059 mTranslateableViews.remove(mChildrenContainerStub);
1060 mTranslateableViews.remove(mGutsStub);
1061 }
1062
Mady Mellor4b80b102016-01-22 08:03:58 -08001063 public void resetTranslation() {
Mady Mellor32c638a2016-09-14 08:58:25 -07001064 if (mTranslateAnim != null) {
1065 mTranslateAnim.cancel();
1066 }
Mady Mellor4b80b102016-01-22 08:03:58 -08001067 if (mTranslateableViews != null) {
1068 for (int i = 0; i < mTranslateableViews.size(); i++) {
1069 mTranslateableViews.get(i).setTranslationX(0);
1070 }
Mady Mellor4b80b102016-01-22 08:03:58 -08001071 }
Mady Mellorb0a82462016-04-30 17:31:02 -07001072 invalidateOutline();
Mady Mellor761cde12017-01-10 11:36:39 -08001073 if (mMenuRow != null) {
1074 mMenuRow.resetState(true /* notify */);
Mady Mellor4b80b102016-01-22 08:03:58 -08001075 }
1076 }
1077
1078 public void animateTranslateNotification(final float leftTarget) {
1079 if (mTranslateAnim != null) {
1080 mTranslateAnim.cancel();
1081 }
Mady Mellorb0a82462016-04-30 17:31:02 -07001082 mTranslateAnim = getTranslateViewAnimator(leftTarget, null /* updateListener */);
Mady Mellor34958fa2016-02-23 09:52:17 -08001083 if (mTranslateAnim != null) {
1084 mTranslateAnim.start();
1085 }
1086 }
1087
1088 @Override
1089 public void setTranslation(float translationX) {
1090 if (areGutsExposed()) {
1091 // Don't translate if guts are showing.
1092 return;
1093 }
1094 // Translate the group of views
1095 for (int i = 0; i < mTranslateableViews.size(); i++) {
1096 if (mTranslateableViews.get(i) != null) {
1097 mTranslateableViews.get(i).setTranslationX(translationX);
1098 }
1099 }
Mady Mellorb0a82462016-04-30 17:31:02 -07001100 invalidateOutline();
Mady Mellor761cde12017-01-10 11:36:39 -08001101 if (mMenuRow != null) {
1102 mMenuRow.updateMenuAlpha(translationX, getMeasuredWidth());
Mady Mellor34958fa2016-02-23 09:52:17 -08001103 }
1104 }
1105
1106 @Override
1107 public float getTranslation() {
1108 if (mTranslateableViews != null && mTranslateableViews.size() > 0) {
1109 // All of the views in the list should have same translation, just use first one.
1110 return mTranslateableViews.get(0).getTranslationX();
1111 }
1112 return 0;
1113 }
1114
1115 public Animator getTranslateViewAnimator(final float leftTarget,
1116 AnimatorUpdateListener listener) {
Mady Mellor723f1f92016-03-13 15:54:06 -07001117 if (mTranslateAnim != null) {
1118 mTranslateAnim.cancel();
1119 }
Mady Mellor34958fa2016-02-23 09:52:17 -08001120 if (areGutsExposed()) {
1121 // No translation if guts are exposed.
1122 return null;
1123 }
Mady Mellorb0a82462016-04-30 17:31:02 -07001124 final ObjectAnimator translateAnim = ObjectAnimator.ofFloat(this, TRANSLATE_CONTENT,
1125 leftTarget);
1126 if (listener != null) {
1127 translateAnim.addUpdateListener(listener);
Mady Mellor4b80b102016-01-22 08:03:58 -08001128 }
Mady Mellorb0a82462016-04-30 17:31:02 -07001129 translateAnim.addListener(new AnimatorListenerAdapter() {
1130 boolean cancelled = false;
1131
1132 @Override
1133 public void onAnimationCancel(Animator anim) {
1134 cancelled = true;
1135 }
1136
1137 @Override
1138 public void onAnimationEnd(Animator anim) {
Mady Mellor761cde12017-01-10 11:36:39 -08001139 if (!cancelled && mMenuRow != null && leftTarget == 0) {
1140 mMenuRow.resetState(true /* notify */);
Mady Mellorb0a82462016-04-30 17:31:02 -07001141 mTranslateAnim = null;
1142 }
1143 }
1144 });
1145 mTranslateAnim = translateAnim;
1146 return translateAnim;
Mady Mellor4b80b102016-01-22 08:03:58 -08001147 }
1148
1149 public float getSpaceForGear() {
Mady Mellor761cde12017-01-10 11:36:39 -08001150 if (mMenuRow != null) {
1151 return mMenuRow.getSpaceForMenu();
Mady Mellor4b80b102016-01-22 08:03:58 -08001152 }
1153 return 0;
1154 }
1155
Mady Mellor761cde12017-01-10 11:36:39 -08001156 public NotificationMenuRow getSettingsRow() {
1157 if (mMenuRow == null) {
1158 mMenuRowStub.inflate();
Mady Mellor4b80b102016-01-22 08:03:58 -08001159 }
Mady Mellor761cde12017-01-10 11:36:39 -08001160 return mMenuRow;
Mady Mellor4b80b102016-01-22 08:03:58 -08001161 }
1162
Selim Cinekab29aeb2015-02-20 18:18:32 +01001163 public void inflateGuts() {
1164 if (mGuts == null) {
1165 mGutsStub.inflate();
1166 }
1167 }
1168
Selim Cinekda42d652015-12-04 15:51:16 -08001169 private void updateChildrenVisibility() {
Selim Cinekd84a5932015-12-15 11:45:36 -08001170 mPrivateLayout.setVisibility(!mShowingPublic && !mIsSummaryWithChildren ? VISIBLE
1171 : INVISIBLE);
Selim Cinekef5127e2015-12-21 16:55:58 -08001172 if (mChildrenContainer != null) {
1173 mChildrenContainer.setVisibility(!mShowingPublic && mIsSummaryWithChildren ? VISIBLE
1174 : INVISIBLE);
Mady Mellorb0a82462016-04-30 17:31:02 -07001175 mChildrenContainer.updateHeaderVisibility(!mShowingPublic && mIsSummaryWithChildren
1176 ? VISIBLE
Selim Cinekef5127e2015-12-21 16:55:58 -08001177 : INVISIBLE);
1178 }
Selim Cinekda42d652015-12-04 15:51:16 -08001179 // The limits might have changed if the view suddenly became a group or vice versa
1180 updateLimits();
Selim Cinekb5605e52015-02-20 18:21:41 +01001181 }
1182
Jorim Jaggife40f7d2014-04-28 15:20:04 +02001183 @Override
Alan Viverettea54956a2015-01-07 16:05:02 -08001184 public boolean onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event) {
1185 if (super.onRequestSendAccessibilityEventInternal(child, event)) {
Jorim Jaggife40f7d2014-04-28 15:20:04 +02001186 // Add a record for the entire layout since its content is somehow small.
1187 // The event comes from a leaf view that is interacted with.
1188 AccessibilityEvent record = AccessibilityEvent.obtain();
1189 onInitializeAccessibilityEvent(record);
1190 dispatchPopulateAccessibilityEvent(record);
1191 event.appendRecord(record);
1192 return true;
1193 }
1194 return false;
Jorim Jaggic5dc0d02014-04-15 15:42:55 +02001195 }
Chris Wren51c75102013-07-16 20:49:17 -04001196
John Spurlocke15452b2014-08-21 09:44:39 -04001197 @Override
Jorim Jaggi4e857f42014-11-17 19:14:04 +01001198 public void setDark(boolean dark, boolean fade, long delay) {
1199 super.setDark(dark, fade, delay);
John Spurlocke15452b2014-08-21 09:44:39 -04001200 final NotificationContentView showing = getShowingLayout();
1201 if (showing != null) {
Jorim Jaggi4e857f42014-11-17 19:14:04 +01001202 showing.setDark(dark, fade, delay);
John Spurlocke15452b2014-08-21 09:44:39 -04001203 }
Selim Cinek9c7712d2015-12-08 19:19:48 -08001204 if (mIsSummaryWithChildren) {
Selim Cinekc897bd32016-03-18 17:32:31 -07001205 mChildrenContainer.setDark(dark, fade, delay);
Selim Cinek9c7712d2015-12-08 19:19:48 -08001206 }
John Spurlocke15452b2014-08-21 09:44:39 -04001207 }
1208
Chris Wren51c75102013-07-16 20:49:17 -04001209 public boolean isExpandable() {
Selim Cinek388df6d2015-10-22 13:25:11 -07001210 if (mIsSummaryWithChildren && !mShowingPublic) {
1211 return !mChildrenExpanded;
1212 }
Chris Wren51c75102013-07-16 20:49:17 -04001213 return mExpandable;
1214 }
1215
1216 public void setExpandable(boolean expandable) {
1217 mExpandable = expandable;
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001218 mPrivateLayout.updateExpandButtons(isExpandable());
Chris Wren51c75102013-07-16 20:49:17 -04001219 }
1220
Selim Cinek4ffd6362015-12-29 15:12:23 +01001221 @Override
1222 public void setClipToActualHeight(boolean clipToActualHeight) {
Selim Cinek084c16b2016-01-22 17:48:22 -08001223 super.setClipToActualHeight(clipToActualHeight || isUserLocked());
1224 getShowingLayout().setClipToActualHeight(clipToActualHeight || isUserLocked());
Selim Cinek4ffd6362015-12-29 15:12:23 +01001225 }
1226
Selim Cinek1685e632014-04-08 02:27:49 +02001227 /**
1228 * @return whether the user has changed the expansion state
1229 */
1230 public boolean hasUserChangedExpansion() {
1231 return mHasUserChangedExpansion;
1232 }
1233
Chris Wren51c75102013-07-16 20:49:17 -04001234 public boolean isUserExpanded() {
1235 return mUserExpanded;
1236 }
1237
Selim Cinek1685e632014-04-08 02:27:49 +02001238 /**
1239 * Set this notification to be expanded by the user
1240 *
1241 * @param userExpanded whether the user wants this notification to be expanded
1242 */
Chris Wren51c75102013-07-16 20:49:17 -04001243 public void setUserExpanded(boolean userExpanded) {
Selim Cinek388df6d2015-10-22 13:25:11 -07001244 setUserExpanded(userExpanded, false /* allowChildExpansion */);
1245 }
1246
1247 /**
1248 * Set this notification to be expanded by the user
1249 *
1250 * @param userExpanded whether the user wants this notification to be expanded
1251 * @param allowChildExpansion whether a call to this method allows expanding children
1252 */
1253 public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -07001254 mFalsingManager.setNotificationExpanded();
Selim Cinek388df6d2015-10-22 13:25:11 -07001255 if (mIsSummaryWithChildren && !mShowingPublic && allowChildExpansion) {
Chris Wren698b1702016-05-23 11:16:32 -04001256 final boolean wasExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
Selim Cinek388df6d2015-10-22 13:25:11 -07001257 mGroupManager.setGroupExpanded(mStatusBarNotification, userExpanded);
Chris Wren698b1702016-05-23 11:16:32 -04001258 logExpansionEvent(true /* userAction */, wasExpanded);
Selim Cinek388df6d2015-10-22 13:25:11 -07001259 return;
1260 }
Christoph Studera7fe6312014-06-27 19:32:44 +02001261 if (userExpanded && !mExpandable) return;
Chris Wren78403d72014-07-28 10:23:24 +01001262 final boolean wasExpanded = isExpanded();
Selim Cinek1685e632014-04-08 02:27:49 +02001263 mHasUserChangedExpansion = true;
Chris Wren51c75102013-07-16 20:49:17 -04001264 mUserExpanded = userExpanded;
Chris Wren78403d72014-07-28 10:23:24 +01001265 logExpansionEvent(true, wasExpanded);
Chris Wren51c75102013-07-16 20:49:17 -04001266 }
1267
Selim Cinekccd14fb2014-08-12 18:53:24 +02001268 public void resetUserExpansion() {
1269 mHasUserChangedExpansion = false;
1270 mUserExpanded = false;
1271 }
1272
Chris Wren51c75102013-07-16 20:49:17 -04001273 public boolean isUserLocked() {
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001274 return mUserLocked && !mForceUnlocked;
Chris Wren51c75102013-07-16 20:49:17 -04001275 }
1276
1277 public void setUserLocked(boolean userLocked) {
1278 mUserLocked = userLocked;
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001279 mPrivateLayout.setUserExpanding(userLocked);
Selim Cinek42357e02016-02-24 18:48:01 -08001280 if (mIsSummaryWithChildren) {
1281 mChildrenContainer.setUserLocked(userLocked);
Selim Cinek7baaa9e2016-07-21 17:21:09 -07001282 if (userLocked || !isGroupExpanded()) {
Mady Mellorb0a82462016-04-30 17:31:02 -07001283 updateBackgroundForGroupState();
1284 }
Selim Cinek42357e02016-02-24 18:48:01 -08001285 }
Chris Wren51c75102013-07-16 20:49:17 -04001286 }
1287
Selim Cinek1685e632014-04-08 02:27:49 +02001288 /**
1289 * @return has the system set this notification to be expanded
1290 */
1291 public boolean isSystemExpanded() {
1292 return mIsSystemExpanded;
1293 }
1294
1295 /**
1296 * Set this notification to be expanded by the system.
1297 *
1298 * @param expand whether the system wants this notification to be expanded.
1299 */
1300 public void setSystemExpanded(boolean expand) {
Selim Cinek31094df2014-08-14 19:28:15 +02001301 if (expand != mIsSystemExpanded) {
1302 final boolean wasExpanded = isExpanded();
1303 mIsSystemExpanded = expand;
Selim Cinekb5605e52015-02-20 18:21:41 +01001304 notifyHeightChanged(false /* needsAnimation */);
Selim Cinek31094df2014-08-14 19:28:15 +02001305 logExpansionEvent(false, wasExpanded);
Selim Cineked6913b2016-06-01 12:01:17 -07001306 if (mIsSummaryWithChildren) {
Selim Cinekc897bd32016-03-18 17:32:31 -07001307 mChildrenContainer.updateGroupOverflow();
1308 }
Selim Cinek31094df2014-08-14 19:28:15 +02001309 }
Jorim Jaggidce3c4c2014-04-29 23:12:24 +02001310 }
1311
1312 /**
Selim Cinek83bc7832015-10-22 13:26:54 -07001313 * @param onKeyguard whether to prevent notification expansion
Jorim Jaggidce3c4c2014-04-29 23:12:24 +02001314 */
Selim Cinek83bc7832015-10-22 13:26:54 -07001315 public void setOnKeyguard(boolean onKeyguard) {
1316 if (onKeyguard != mOnKeyguard) {
Selim Cinek31094df2014-08-14 19:28:15 +02001317 final boolean wasExpanded = isExpanded();
Selim Cinek83bc7832015-10-22 13:26:54 -07001318 mOnKeyguard = onKeyguard;
Selim Cinek31094df2014-08-14 19:28:15 +02001319 logExpansionEvent(false, wasExpanded);
1320 if (wasExpanded != isExpanded()) {
Selim Cinekc897bd32016-03-18 17:32:31 -07001321 if (mIsSummaryWithChildren) {
1322 mChildrenContainer.updateGroupOverflow();
1323 }
Mady Mellor4b80b102016-01-22 08:03:58 -08001324 notifyHeightChanged(false /* needsAnimation */);
Selim Cinek31094df2014-08-14 19:28:15 +02001325 }
1326 }
Selim Cinek1685e632014-04-08 02:27:49 +02001327 }
1328
1329 /**
Selim Cinek9e624e72016-07-20 13:46:49 -07001330 * @return Can the underlying notification be cleared? This can be different from whether the
1331 * notification can be dismissed in case notifications are sensitive on the lockscreen.
1332 * @see #canViewBeDismissed()
Dan Sandler0d3e62f2014-07-14 17:13:50 -04001333 */
1334 public boolean isClearable() {
Selim Cinek506deb62016-07-20 15:43:59 -07001335 if (mStatusBarNotification == null || !mStatusBarNotification.isClearable()) {
1336 return false;
1337 }
1338 if (mIsSummaryWithChildren) {
1339 List<ExpandableNotificationRow> notificationChildren =
1340 mChildrenContainer.getNotificationChildren();
1341 for (int i = 0; i < notificationChildren.size(); i++) {
1342 ExpandableNotificationRow child = notificationChildren.get(i);
1343 if (!child.isClearable()) {
1344 return false;
1345 }
1346 }
1347 }
1348 return true;
Dan Sandler0d3e62f2014-07-14 17:13:50 -04001349 }
1350
Jorim Jaggi9cbadd32014-05-01 20:18:31 +02001351 @Override
1352 public int getIntrinsicHeight() {
Jorim Jaggibe565df2014-04-28 17:51:23 +02001353 if (isUserLocked()) {
1354 return getActualHeight();
1355 }
Geoffrey Pitsch4dd50062016-12-06 16:41:22 -05001356 if (mGuts != null && mGuts.isExposed()) {
Selim Cinekd84a5932015-12-15 11:45:36 -08001357 return mGuts.getHeight();
1358 } else if ((isChildInGroup() && !isGroupExpanded())) {
1359 return mPrivateLayout.getMinHeight();
Adrian Roos0aac04f2016-12-08 15:59:29 -08001360 } else if (mShowAmbient) {
1361 return getAmbientHeight();
Selim Cinekd84a5932015-12-15 11:45:36 -08001362 } else if (mSensitive && mHideSensitiveForIntrinsicHeight) {
1363 return getMinHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -07001364 } else if (mIsSummaryWithChildren && !mOnKeyguard) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001365 return mChildrenContainer.getIntrinsicHeight();
Selim Cinek01ee2cd52016-12-21 18:23:11 +01001366 } else if (!mOnKeyguard && (mIsHeadsUp || mHeadsupDisappearRunning)) {
Selim Cinek73cf02a2016-06-17 13:08:00 -07001367 if (isPinned() || mHeadsupDisappearRunning) {
Selim Cinek31aada42015-12-18 17:51:15 -08001368 return getPinnedHeadsUpHeight(true /* atLeastMinHeight */);
1369 } else if (isExpanded()) {
Selim Cinekd84a5932015-12-15 11:45:36 -08001370 return Math.max(getMaxExpandHeight(), mHeadsUpHeight);
Selim Cinek8d490d42015-04-10 00:05:50 -07001371 } else {
Selim Cinek567e8452016-03-24 10:54:56 -07001372 return Math.max(getCollapsedHeight(), mHeadsUpHeight);
Selim Cinek8d490d42015-04-10 00:05:50 -07001373 }
Selim Cinek31aada42015-12-18 17:51:15 -08001374 } else if (isExpanded()) {
Selim Cinek83bc7832015-10-22 13:26:54 -07001375 return getMaxExpandHeight();
Selim Cinekd84a5932015-12-15 11:45:36 -08001376 } else {
Selim Cinek567e8452016-03-24 10:54:56 -07001377 return getCollapsedHeight();
Selim Cinek1685e632014-04-08 02:27:49 +02001378 }
Selim Cinekb5605e52015-02-20 18:21:41 +01001379 }
Selim Cinek1685e632014-04-08 02:27:49 +02001380
Mady Mellor43c2cd12016-12-12 21:05:13 -08001381 @Override
Mady Mellorb0a82462016-04-30 17:31:02 -07001382 public boolean isGroupExpanded() {
Selim Cinekeef84282015-10-30 16:28:00 -07001383 return mGroupManager.isGroupExpanded(mStatusBarNotification);
Selim Cinekb5605e52015-02-20 18:21:41 +01001384 }
1385
Selim Cinek263398f2015-10-21 17:40:23 -07001386 private void onChildrenCountChanged() {
Selim Cinek8fc93c92015-11-23 17:48:07 -08001387 mIsSummaryWithChildren = BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS
Mady Mellorb0a82462016-04-30 17:31:02 -07001388 && mChildrenContainer != null && mChildrenContainer.getNotificationChildCount() > 0;
1389 if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() == null) {
1390 mChildrenContainer.recreateNotificationHeader(mExpandClickListener,
1391 mEntry.notification);
Selim Cinek263398f2015-10-21 17:40:23 -07001392 }
Mady Mellor6baed9e2016-05-25 16:05:09 -07001393 getShowingLayout().updateBackgroundColor(false /* animate */);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001394 mPrivateLayout.updateExpandButtons(isExpandable());
Selim Cinekea4bef72015-12-02 15:51:10 -08001395 updateChildrenHeaderAppearance();
Selim Cinekda42d652015-12-04 15:51:16 -08001396 updateChildrenVisibility();
Selim Cinek263398f2015-10-21 17:40:23 -07001397 }
1398
Mady Mellorb0a82462016-04-30 17:31:02 -07001399 public void updateChildrenHeaderAppearance() {
Selim Cineked6913b2016-06-01 12:01:17 -07001400 if (mIsSummaryWithChildren) {
Mady Mellorb0a82462016-04-30 17:31:02 -07001401 mChildrenContainer.updateChildrenHeaderAppearance();
1402 }
1403 }
1404
Selim Cinek1685e632014-04-08 02:27:49 +02001405 /**
1406 * Check whether the view state is currently expanded. This is given by the system in {@link
1407 * #setSystemExpanded(boolean)} and can be overridden by user expansion or
1408 * collapsing in {@link #setUserExpanded(boolean)}. Note that the visual appearance of this
1409 * view can differ from this state, if layout params are modified from outside.
1410 *
1411 * @return whether the view state is currently expanded.
1412 */
Selim Cinek83bc7832015-10-22 13:26:54 -07001413 public boolean isExpanded() {
Selim Cineke81b82b2016-03-04 11:22:28 -08001414 return isExpanded(false /* allowOnKeyguard */);
1415 }
1416
1417 public boolean isExpanded(boolean allowOnKeyguard) {
1418 return (!mOnKeyguard || allowOnKeyguard)
Selim Cinekb5605e52015-02-20 18:21:41 +01001419 && (!hasUserChangedExpansion() && (isSystemExpanded() || isSystemChildExpanded())
1420 || isUserExpanded());
1421 }
1422
1423 private boolean isSystemChildExpanded() {
1424 return mIsSystemChildExpanded;
1425 }
1426
1427 public void setSystemChildExpanded(boolean expanded) {
1428 mIsSystemChildExpanded = expanded;
Selim Cinek1685e632014-04-08 02:27:49 +02001429 }
1430
1431 @Override
1432 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
1433 super.onLayout(changed, left, top, right, bottom);
Selim Cinek8d490d42015-04-10 00:05:50 -07001434 updateMaxHeights();
Mady Mellor761cde12017-01-10 11:36:39 -08001435 if (mMenuRow != null) {
1436 mMenuRow.updateVerticalLocation();
Mady Mellora6edc872016-04-26 11:01:03 -07001437 }
Selim Cinek875a3a12016-11-18 17:52:16 -08001438 updateContentShiftHeight();
1439 }
1440
1441 /**
1442 * Updates the content shift height such that the header is completely hidden when coming from
1443 * the top.
1444 */
1445 private void updateContentShiftHeight() {
1446 NotificationHeaderView notificationHeader = getNotificationHeader();
1447 if (notificationHeader != null) {
1448 CachingIconView icon = notificationHeader.getIcon();
1449 mIconTransformContentShift = getRelativeTopPadding(icon) + icon.getHeight();
1450 } else {
1451 mIconTransformContentShift = mIconTransformContentShiftNoIcon;
1452 }
Selim Cinek1685e632014-04-08 02:27:49 +02001453 }
1454
Selim Cinek8d490d42015-04-10 00:05:50 -07001455 private void updateMaxHeights() {
Selim Cinekd2319fb2014-09-01 19:41:54 +02001456 int intrinsicBefore = getIntrinsicHeight();
Selim Cinek8d490d42015-04-10 00:05:50 -07001457 View expandedChild = mPrivateLayout.getExpandedChild();
1458 if (expandedChild == null) {
1459 expandedChild = mPrivateLayout.getContractedChild();
1460 }
1461 mMaxExpandHeight = expandedChild.getHeight();
1462 View headsUpChild = mPrivateLayout.getHeadsUpChild();
Selim Cinek1f3f5442015-04-10 17:54:46 -07001463 if (headsUpChild == null) {
1464 headsUpChild = mPrivateLayout.getContractedChild();
Selim Cinek8d490d42015-04-10 00:05:50 -07001465 }
Selim Cinek1f3f5442015-04-10 17:54:46 -07001466 mHeadsUpHeight = headsUpChild.getHeight();
Selim Cinekd2319fb2014-09-01 19:41:54 +02001467 if (intrinsicBefore != getIntrinsicHeight()) {
Selim Cinekbb42b7d2016-08-10 13:02:38 -07001468 notifyHeightChanged(true /* needsAnimation */);
Selim Cinekd2319fb2014-09-01 19:41:54 +02001469 }
1470 }
1471
Selim Cinekfa0a2d32016-01-14 13:02:21 -08001472 @Override
1473 public void notifyHeightChanged(boolean needsAnimation) {
1474 super.notifyHeightChanged(needsAnimation);
1475 getShowingLayout().requestSelectLayout(needsAnimation || isUserLocked());
1476 }
1477
Selim Cinek3c76d502016-02-19 15:16:33 -08001478 public void setSensitive(boolean sensitive, boolean hideSensitive) {
Jorim Jaggiae441282014-08-01 02:45:18 +02001479 mSensitive = sensitive;
Selim Cinek3c76d502016-02-19 15:16:33 -08001480 mSensitiveHiddenInGeneral = hideSensitive;
Jorim Jaggiae441282014-08-01 02:45:18 +02001481 }
1482
Mady Mellor43c2cd12016-12-12 21:05:13 -08001483 @Override
Jorim Jaggiae441282014-08-01 02:45:18 +02001484 public void setHideSensitiveForIntrinsicHeight(boolean hideSensitive) {
Selim Cinek60122be2015-04-15 18:16:50 -07001485 mHideSensitiveForIntrinsicHeight = hideSensitive;
Selim Cineka52f6a12016-02-29 15:35:58 -08001486 if (mIsSummaryWithChildren) {
1487 List<ExpandableNotificationRow> notificationChildren =
1488 mChildrenContainer.getNotificationChildren();
1489 for (int i = 0; i < notificationChildren.size(); i++) {
1490 ExpandableNotificationRow child = notificationChildren.get(i);
1491 child.setHideSensitiveForIntrinsicHeight(hideSensitive);
1492 }
1493 }
Jorim Jaggiae441282014-08-01 02:45:18 +02001494 }
1495
Mady Mellor43c2cd12016-12-12 21:05:13 -08001496 @Override
Jorim Jaggiae441282014-08-01 02:45:18 +02001497 public void setHideSensitive(boolean hideSensitive, boolean animated, long delay,
1498 long duration) {
1499 boolean oldShowingPublic = mShowingPublic;
1500 mShowingPublic = mSensitive && hideSensitive;
1501 if (mShowingPublicInitialized && mShowingPublic == oldShowingPublic) {
1502 return;
1503 }
Dan Sandlera5e0f412014-01-23 15:11:54 -05001504
1505 // bail out if no public version
Selim Cinek1685e632014-04-08 02:27:49 +02001506 if (mPublicLayout.getChildCount() == 0) return;
Dan Sandlera5e0f412014-01-23 15:11:54 -05001507
Jorim Jaggiae441282014-08-01 02:45:18 +02001508 if (!animated) {
1509 mPublicLayout.animate().cancel();
1510 mPrivateLayout.animate().cancel();
Selim Cineka554c702016-06-17 18:02:12 -07001511 if (mChildrenContainer != null) {
1512 mChildrenContainer.animate().cancel();
1513 mChildrenContainer.setAlpha(1f);
1514 }
Jorim Jaggiae441282014-08-01 02:45:18 +02001515 mPublicLayout.setAlpha(1f);
1516 mPrivateLayout.setAlpha(1f);
1517 mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE);
Selim Cinekd84a5932015-12-15 11:45:36 -08001518 updateChildrenVisibility();
Jorim Jaggiae441282014-08-01 02:45:18 +02001519 } else {
1520 animateShowingPublic(delay, duration);
1521 }
Selim Cinekc3179332016-03-04 14:44:56 -08001522 NotificationContentView showingLayout = getShowingLayout();
1523 showingLayout.updateBackgroundColor(animated);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001524 mPrivateLayout.updateExpandButtons(isExpandable());
Adrian Roose5726a22016-12-19 14:26:51 -08001525 showingLayout.setDark(isDark(), false /* animate */, 0 /* delay */);
Jorim Jaggiae441282014-08-01 02:45:18 +02001526 mShowingPublicInitialized = true;
1527 }
1528
1529 private void animateShowingPublic(long delay, long duration) {
Mady Mellorb0a82462016-04-30 17:31:02 -07001530 View[] privateViews = mIsSummaryWithChildren
1531 ? new View[] {mChildrenContainer}
Selim Cinekd84a5932015-12-15 11:45:36 -08001532 : new View[] {mPrivateLayout};
1533 View[] publicViews = new View[] {mPublicLayout};
1534 View[] hiddenChildren = mShowingPublic ? privateViews : publicViews;
1535 View[] shownChildren = mShowingPublic ? publicViews : privateViews;
1536 for (final View hiddenView : hiddenChildren) {
1537 hiddenView.setVisibility(View.VISIBLE);
1538 hiddenView.animate().cancel();
1539 hiddenView.animate()
1540 .alpha(0f)
1541 .setStartDelay(delay)
1542 .setDuration(duration)
1543 .withEndAction(new Runnable() {
1544 @Override
1545 public void run() {
1546 hiddenView.setVisibility(View.INVISIBLE);
1547 }
1548 });
1549 }
1550 for (View showView : shownChildren) {
1551 showView.setVisibility(View.VISIBLE);
1552 showView.setAlpha(0f);
1553 showView.animate().cancel();
1554 showView.animate()
1555 .alpha(1f)
1556 .setStartDelay(delay)
1557 .setDuration(duration);
1558 }
Dan Sandler0d3e62f2014-07-14 17:13:50 -04001559 }
1560
Mady Mellor43c2cd12016-12-12 21:05:13 -08001561 @Override
Selim Cinek3776fe02016-02-04 13:32:43 -08001562 public boolean mustStayOnScreen() {
1563 return mIsHeadsUp;
1564 }
1565
Selim Cinek9e624e72016-07-20 13:46:49 -07001566 /**
1567 * @return Whether this view is allowed to be dismissed. Only valid for visible notifications as
1568 * otherwise some state might not be updated. To request about the general clearability
1569 * see {@link #isClearable()}.
1570 */
1571 public boolean canViewBeDismissed() {
Selim Cineke9bad242016-06-15 11:46:37 -07001572 return isClearable() && (!mShowingPublic || !mSensitiveHiddenInGeneral);
Dan Sandlera5e0f412014-01-23 15:11:54 -05001573 }
Jorim Jaggi251957d2014-04-09 04:24:09 +02001574
Ricky Waicd35def2016-05-03 11:07:07 +01001575 public void makeActionsVisibile() {
1576 setUserExpanded(true, true);
1577 if (isChildInGroup()) {
1578 mGroupManager.setGroupExpanded(mStatusBarNotification, true);
1579 }
Selim Cinekbb42b7d2016-08-10 13:02:38 -07001580 notifyHeightChanged(false /* needsAnimation */);
Ricky Waicd35def2016-05-03 11:07:07 +01001581 }
1582
Selim Cinekb5605e52015-02-20 18:21:41 +01001583 public void setChildrenExpanded(boolean expanded, boolean animate) {
1584 mChildrenExpanded = expanded;
Selim Cinek83bc7832015-10-22 13:26:54 -07001585 if (mChildrenContainer != null) {
1586 mChildrenContainer.setChildrenExpanded(expanded);
1587 }
Mady Mellor1a5d8ea2016-06-09 10:42:42 -07001588 updateBackgroundForGroupState();
Selim Cinekddf1b392016-05-27 16:33:10 -07001589 updateClickAndFocus();
Selim Cinekb5605e52015-02-20 18:21:41 +01001590 }
1591
Selim Cinekb5605e52015-02-20 18:21:41 +01001592 public static void applyTint(View v, int color) {
1593 int alpha;
1594 if (color != 0) {
1595 alpha = COLORED_DIVIDER_ALPHA;
1596 } else {
1597 color = 0xff000000;
1598 alpha = DEFAULT_DIVIDER_ALPHA;
1599 }
1600 if (v.getBackground() instanceof ColorDrawable) {
1601 ColorDrawable background = (ColorDrawable) v.getBackground();
1602 background.mutate();
1603 background.setColor(color);
1604 background.setAlpha(alpha);
1605 }
1606 }
1607
Selim Cinek1685e632014-04-08 02:27:49 +02001608 public int getMaxExpandHeight() {
Selim Cinekb5605e52015-02-20 18:21:41 +01001609 return mMaxExpandHeight;
Chris Wren51c75102013-07-16 20:49:17 -04001610 }
Jorim Jaggi584a7aa2014-04-10 23:26:13 +02001611
Mady Mellor34958fa2016-02-23 09:52:17 -08001612 public boolean areGutsExposed() {
Geoffrey Pitsch4dd50062016-12-06 16:41:22 -05001613 return (mGuts != null && mGuts.isExposed());
Mady Mellor34958fa2016-02-23 09:52:17 -08001614 }
1615
Jorim Jaggibe565df2014-04-28 17:51:23 +02001616 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001617 public boolean isContentExpandable() {
Selim Cinek2f0df8a2014-06-10 17:40:42 +02001618 NotificationContentView showingLayout = getShowingLayout();
1619 return showingLayout.isContentExpandable();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001620 }
1621
1622 @Override
Selim Cinek560e64d2015-06-09 19:58:11 -07001623 protected View getContentView() {
Selim Cinekaa3901a2016-08-05 11:04:37 -07001624 if (mIsSummaryWithChildren && !mShowingPublic) {
Selim Cineka5703182016-05-11 21:23:16 -04001625 return mChildrenContainer;
1626 }
Selim Cinek560e64d2015-06-09 19:58:11 -07001627 return getShowingLayout();
1628 }
1629
1630 @Override
Selim Cinekaa3901a2016-08-05 11:04:37 -07001631 protected void onAppearAnimationFinished(boolean wasAppearing) {
1632 super.onAppearAnimationFinished(wasAppearing);
1633 if (wasAppearing) {
1634 // During the animation the visible view might have changed, so let's make sure all
1635 // alphas are reset
1636 if (mChildrenContainer != null) {
1637 mChildrenContainer.setAlpha(1.0f);
1638 mChildrenContainer.setLayerType(LAYER_TYPE_NONE, null);
1639 }
Adrian Rooseb434ff2017-01-11 11:18:48 -08001640 for (NotificationContentView l : mLayouts) {
1641 l.setAlpha(1.0f);
1642 l.setLayerType(LAYER_TYPE_NONE, null);
1643 }
Selim Cinekaa3901a2016-08-05 11:04:37 -07001644 }
1645 }
1646
1647 @Override
Mady Mellorb0a82462016-04-30 17:31:02 -07001648 public int getExtraBottomPadding() {
1649 if (mIsSummaryWithChildren && isGroupExpanded()) {
1650 return mIncreasedPaddingBetweenElements;
1651 }
1652 return 0;
1653 }
1654
1655 @Override
Jorim Jaggid552d9d2014-05-07 19:41:13 +02001656 public void setActualHeight(int height, boolean notifyListeners) {
Selim Cinekb5605e52015-02-20 18:21:41 +01001657 super.setActualHeight(height, notifyListeners);
Geoffrey Pitsch4dd50062016-12-06 16:41:22 -05001658 if (mGuts != null && mGuts.isExposed()) {
Mady Mellorb53bc272016-02-11 18:28:23 -08001659 mGuts.setActualHeight(height);
1660 return;
1661 }
Selim Cinekeef84282015-10-30 16:28:00 -07001662 int contentHeight = Math.max(getMinHeight(), height);
Adrian Rooseb434ff2017-01-11 11:18:48 -08001663 for (NotificationContentView l : mLayouts) {
1664 l.setContentHeight(contentHeight);
1665 }
Selim Cinek42357e02016-02-24 18:48:01 -08001666 if (mIsSummaryWithChildren) {
1667 mChildrenContainer.setActualHeight(height);
1668 }
Jorim Jaggib1cd3c12014-09-08 19:55:17 +02001669 if (mGuts != null) {
1670 mGuts.setActualHeight(height);
1671 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001672 }
1673
1674 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01001675 public int getMaxContentHeight() {
Selim Cinek83bc7832015-10-22 13:26:54 -07001676 if (mIsSummaryWithChildren && !mShowingPublic) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001677 return mChildrenContainer.getMaxContentHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -07001678 }
Selim Cinek2f0df8a2014-06-10 17:40:42 +02001679 NotificationContentView showingLayout = getShowingLayout();
1680 return showingLayout.getMaxHeight();
Jorim Jaggibe565df2014-04-28 17:51:23 +02001681 }
1682
1683 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001684 public int getMinHeight() {
Selim Cinek01ee2cd52016-12-21 18:23:11 +01001685 if (!mOnKeyguard && mIsHeadsUp && mHeadsUpManager.isTrackingHeadsUp()) {
Selim Cinek31aada42015-12-18 17:51:15 -08001686 return getPinnedHeadsUpHeight(false /* atLeastMinHeight */);
1687 } else if (mIsSummaryWithChildren && !isGroupExpanded() && !mShowingPublic) {
Selim Cinekb55386d2015-12-16 17:26:49 -08001688 return mChildrenContainer.getMinHeight();
Selim Cinek01ee2cd52016-12-21 18:23:11 +01001689 } else if (!mOnKeyguard && mIsHeadsUp) {
Selim Cinek31aada42015-12-18 17:51:15 -08001690 return mHeadsUpHeight;
Selim Cinekb55386d2015-12-16 17:26:49 -08001691 }
Selim Cinek816c8e42015-11-19 12:00:45 -08001692 NotificationContentView showingLayout = getShowingLayout();
1693 return showingLayout.getMinHeight();
1694 }
1695
Adrian Roos0aac04f2016-12-08 15:59:29 -08001696 private int getAmbientHeight() {
1697 NotificationContentView showingLayout = getShowingLayout();
1698 return showingLayout.getAmbientChild() != null
1699 ? showingLayout.getAmbientChild().getHeight()
1700 : getCollapsedHeight();
1701 }
1702
Selim Cinek816c8e42015-11-19 12:00:45 -08001703 @Override
Selim Cinek567e8452016-03-24 10:54:56 -07001704 public int getCollapsedHeight() {
Selim Cinek2c584612016-02-29 16:14:25 -08001705 if (mIsSummaryWithChildren && !mShowingPublic) {
Selim Cinek567e8452016-03-24 10:54:56 -07001706 return mChildrenContainer.getCollapsedHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -07001707 }
Selim Cinek816c8e42015-11-19 12:00:45 -08001708 return getMinHeight();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001709 }
1710
1711 @Override
Jorim Jaggibe565df2014-04-28 17:51:23 +02001712 public void setClipTopAmount(int clipTopAmount) {
1713 super.setClipTopAmount(clipTopAmount);
Adrian Rooseb434ff2017-01-11 11:18:48 -08001714 for (NotificationContentView l : mLayouts) {
1715 l.setClipTopAmount(clipTopAmount);
1716 }
Jorim Jaggib1cd3c12014-09-08 19:55:17 +02001717 if (mGuts != null) {
1718 mGuts.setClipTopAmount(clipTopAmount);
1719 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001720 }
1721
Selim Cineka686b2c2016-10-26 13:58:27 -07001722 @Override
1723 public void setClipBottomAmount(int clipBottomAmount) {
Selim Cinek65d418e2016-11-29 15:42:34 -08001724 if (clipBottomAmount != mClipBottomAmount) {
1725 super.setClipBottomAmount(clipBottomAmount);
Adrian Rooseb434ff2017-01-11 11:18:48 -08001726 for (NotificationContentView l : mLayouts) {
1727 l.setClipBottomAmount(clipBottomAmount);
1728 }
Selim Cinek65d418e2016-11-29 15:42:34 -08001729 if (mGuts != null) {
1730 mGuts.setClipBottomAmount(clipBottomAmount);
1731 }
Selim Cineka686b2c2016-10-26 13:58:27 -07001732 }
Selim Cinekb3dadcc2016-11-21 17:21:13 -08001733 if (mChildrenContainer != null) {
Selim Cinek65d418e2016-11-29 15:42:34 -08001734 // We have to update this even if it hasn't changed, since the children locations can
1735 // have changed
Selim Cinekb3dadcc2016-11-21 17:21:13 -08001736 mChildrenContainer.setClipBottomAmount(clipBottomAmount);
1737 }
Selim Cineka686b2c2016-10-26 13:58:27 -07001738 }
1739
Selim Cinek31094df2014-08-14 19:28:15 +02001740 public boolean isMaxExpandHeightInitialized() {
1741 return mMaxExpandHeight != 0;
Selim Cinek7d447722014-06-10 15:51:59 +02001742 }
Selim Cinek2f0df8a2014-06-10 17:40:42 +02001743
Selim Cinek42357e02016-02-24 18:48:01 -08001744 public NotificationContentView getShowingLayout() {
Selim Cinek2f0df8a2014-06-10 17:40:42 +02001745 return mShowingPublic ? mPublicLayout : mPrivateLayout;
1746 }
Chris Wren78403d72014-07-28 10:23:24 +01001747
Jorim Jaggi59ec3042015-06-05 15:18:43 -07001748 @Override
1749 public void setShowingLegacyBackground(boolean showing) {
1750 super.setShowingLegacyBackground(showing);
Adrian Rooseb434ff2017-01-11 11:18:48 -08001751 for (NotificationContentView l : mLayouts) {
1752 l.setShowingLegacyBackground(showing);
1753 }
Jorim Jaggi59ec3042015-06-05 15:18:43 -07001754 }
1755
Selim Cineka6c6bfb2015-10-29 16:27:08 -07001756 @Override
1757 protected void updateBackgroundTint() {
1758 super.updateBackgroundTint();
Mady Mellorb0a82462016-04-30 17:31:02 -07001759 updateBackgroundForGroupState();
Selim Cineka6c6bfb2015-10-29 16:27:08 -07001760 if (mIsSummaryWithChildren) {
1761 List<ExpandableNotificationRow> notificationChildren =
1762 mChildrenContainer.getNotificationChildren();
1763 for (int i = 0; i < notificationChildren.size(); i++) {
1764 ExpandableNotificationRow child = notificationChildren.get(i);
Mady Mellorb0a82462016-04-30 17:31:02 -07001765 child.updateBackgroundForGroupState();
Selim Cineka6c6bfb2015-10-29 16:27:08 -07001766 }
1767 }
1768 }
1769
Mady Mellorb0a82462016-04-30 17:31:02 -07001770 /**
1771 * Called when a group has finished animating from collapsed or expanded state.
1772 */
1773 public void onFinishedExpansionChange() {
1774 mGroupExpansionChanging = false;
1775 updateBackgroundForGroupState();
1776 }
1777
1778 /**
1779 * Updates the parent and children backgrounds in a group based on the expansion state.
1780 */
1781 public void updateBackgroundForGroupState() {
1782 if (mIsSummaryWithChildren) {
1783 // Only when the group has finished expanding do we hide its background.
1784 mShowNoBackground = isGroupExpanded() && !isGroupExpansionChanging() && !isUserLocked();
1785 mChildrenContainer.updateHeaderForExpansion(mShowNoBackground);
1786 List<ExpandableNotificationRow> children = mChildrenContainer.getNotificationChildren();
1787 for (int i = 0; i < children.size(); i++) {
1788 children.get(i).updateBackgroundForGroupState();
1789 }
1790 } else if (isChildInGroup()) {
1791 final int childColor = getShowingLayout().getBackgroundColorForExpansionState();
1792 // Only show a background if the group is expanded OR if it is expanding / collapsing
1793 // and has a custom background color
1794 final boolean showBackground = isGroupExpanded()
1795 || ((mNotificationParent.isGroupExpansionChanging()
1796 || mNotificationParent.isUserLocked()) && childColor != 0);
1797 mShowNoBackground = !showBackground;
1798 } else {
1799 // Only children or parents ever need no background.
1800 mShowNoBackground = false;
1801 }
1802 updateOutline();
Selim Cineka6c6bfb2015-10-29 16:27:08 -07001803 updateBackground();
1804 }
1805
Adrian Roos4a579672016-05-24 16:54:37 -07001806 public int getPositionOfChild(ExpandableNotificationRow childRow) {
1807 if (mIsSummaryWithChildren) {
1808 return mChildrenContainer.getPositionInLinearLayout(childRow);
1809 }
1810 return 0;
1811 }
1812
Chris Wren78403d72014-07-28 10:23:24 +01001813 public void setExpansionLogger(ExpansionLogger logger, String key) {
1814 mLogger = logger;
1815 mLoggingKey = key;
1816 }
1817
Chris Wren6abeeb92016-05-26 14:44:38 -04001818 public void onExpandedByGesture(boolean userExpanded) {
1819 int event = MetricsEvent.ACTION_NOTIFICATION_GESTURE_EXPANDER;
1820 if (mGroupManager.isSummaryOfGroup(getStatusBarNotification())) {
1821 event = MetricsEvent.ACTION_NOTIFICATION_GROUP_GESTURE_EXPANDER;
1822 }
1823 MetricsLogger.action(mContext, event, userExpanded);
1824 }
1825
Selim Cinek6183d122016-01-14 18:48:41 -08001826 @Override
Selim Cinek42357e02016-02-24 18:48:01 -08001827 public float getIncreasedPaddingAmount() {
1828 if (mIsSummaryWithChildren) {
1829 if (isGroupExpanded()) {
1830 return 1.0f;
1831 } else if (isUserLocked()) {
Selim Cinekf07d0622016-03-21 19:52:52 -07001832 return mChildrenContainer.getGroupExpandFraction();
Selim Cinek42357e02016-02-24 18:48:01 -08001833 }
1834 }
1835 return 0.0f;
Selim Cinek61633a82016-01-25 15:54:10 -08001836 }
1837
1838 @Override
Selim Cinek6183d122016-01-14 18:48:41 -08001839 protected boolean disallowSingleClick(MotionEvent event) {
1840 float x = event.getX();
1841 float y = event.getY();
Selim Cinek34eda5e2016-02-18 17:10:43 -08001842 NotificationHeaderView header = getVisibleNotificationHeader();
Selim Cinek6183d122016-01-14 18:48:41 -08001843 if (header != null) {
Mady Mellora8833512016-03-09 09:50:18 -08001844 return header.isInTouchRect(x - getTranslation(), y);
Selim Cinek6183d122016-01-14 18:48:41 -08001845 }
1846 return super.disallowSingleClick(event);
1847 }
1848
Chris Wren78403d72014-07-28 10:23:24 +01001849 private void logExpansionEvent(boolean userAction, boolean wasExpanded) {
Chris Wren698b1702016-05-23 11:16:32 -04001850 boolean nowExpanded = isExpanded();
1851 if (mIsSummaryWithChildren) {
1852 nowExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
1853 }
Chris Wren78403d72014-07-28 10:23:24 +01001854 if (wasExpanded != nowExpanded && mLogger != null) {
1855 mLogger.logNotificationExpansion(mLoggingKey, userAction, nowExpanded) ;
1856 }
1857 }
Selim Cinek570981d2015-12-01 11:37:01 -08001858
Selim Cineke9bad242016-06-15 11:46:37 -07001859 @Override
1860 public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
1861 super.onInitializeAccessibilityNodeInfoInternal(info);
1862 if (canViewBeDismissed()) {
1863 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS);
1864 }
1865 }
1866
1867 @Override
1868 public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
1869 if (super.performAccessibilityActionInternal(action, arguments)) {
1870 return true;
1871 }
1872 switch (action) {
1873 case AccessibilityNodeInfo.ACTION_DISMISS:
1874 NotificationStackScrollLayout.performDismiss(this, mGroupManager,
1875 true /* fromAccessibility */);
1876 return true;
1877 }
1878 return false;
1879 }
1880
1881 public boolean shouldRefocusOnDismiss() {
1882 return mRefocusOnDismiss || isAccessibilityFocused();
1883 }
1884
Selim Cinek570981d2015-12-01 11:37:01 -08001885 public interface OnExpandClickListener {
Selim Cinek31aada42015-12-18 17:51:15 -08001886 void onExpandClicked(NotificationData.Entry clickedEntry, boolean nowExpanded);
Selim Cinek570981d2015-12-01 11:37:01 -08001887 }
Selim Cinekbbcebde2016-11-09 18:28:20 -08001888
1889 @Override
1890 public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
1891 return new NotificationViewState(stackScrollState);
1892 }
1893
Selim Cinekd127d792016-11-01 19:11:41 -07001894 @Override
1895 public boolean isAboveShelf() {
1896 return mIsPinned || mHeadsupDisappearRunning || (mIsHeadsUp && mAboveShelf);
1897 }
1898
Adrian Roos0aac04f2016-12-08 15:59:29 -08001899 public void setShowAmbient(boolean showAmbient) {
1900 if (showAmbient != mShowAmbient) {
1901 mShowAmbient = showAmbient;
1902 notifyHeightChanged(false /* needsAnimation */);
1903 }
1904 }
1905
Selim Cinekd127d792016-11-01 19:11:41 -07001906 public void setAboveShelf(boolean aboveShelf) {
1907 mAboveShelf = aboveShelf;
1908 }
1909
Selim Cinek0cfbef42016-11-09 19:06:36 -08001910 public class NotificationViewState extends ExpandableViewState {
Selim Cinekbbcebde2016-11-09 18:28:20 -08001911
1912 private final StackScrollState mOverallState;
Selim Cinek0242fbb2016-10-19 13:38:32 -07001913
Selim Cinekbbcebde2016-11-09 18:28:20 -08001914
1915 private NotificationViewState(StackScrollState stackScrollState) {
1916 mOverallState = stackScrollState;
1917 }
1918
1919 @Override
1920 public void applyToView(View view) {
1921 super.applyToView(view);
1922 if (view instanceof ExpandableNotificationRow) {
1923 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekbbcebde2016-11-09 18:28:20 -08001924 row.applyChildrenState(mOverallState);
1925 }
1926 }
Selim Cinek0cfbef42016-11-09 19:06:36 -08001927
1928 @Override
Selim Cinek2b549f42016-11-22 16:38:51 -08001929 protected void onYTranslationAnimationFinished(View view) {
1930 super.onYTranslationAnimationFinished(view);
Selim Cinek0cfbef42016-11-09 19:06:36 -08001931 if (mHeadsupDisappearRunning) {
Selim Cinekcafa87f2016-10-26 17:00:17 -07001932 setHeadsUpAnimatingAway(false);
Selim Cinek0cfbef42016-11-09 19:06:36 -08001933 }
1934 }
1935
1936 @Override
1937 public void animateTo(View child, AnimationProperties properties) {
1938 super.animateTo(child, properties);
1939 if (child instanceof ExpandableNotificationRow) {
1940 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
1941 row.startChildAnimation(mOverallState, properties);
1942 }
1943 }
Selim Cinekbbcebde2016-11-09 18:28:20 -08001944 }
Chris Wren51c75102013-07-16 20:49:17 -04001945}