blob: ed4f6855c515d76935a6d074ff0b2d12dca3189c [file] [log] [blame]
Chris Wren51c75102013-07-16 20:49:17 -04001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.systemui.statusbar;
18
Selim Cinek5ba22542017-04-20 15:16:10 -070019import static com.android.systemui.statusbar.notification.NotificationInflater.InflationCallback;
Selim Cinekc478f902017-02-22 20:55:44 -080020
Mady Mellor4b80b102016-01-22 08:03:58 -080021import android.animation.Animator;
22import android.animation.AnimatorListenerAdapter;
Mady Mellor4b80b102016-01-22 08:03:58 -080023import android.animation.ObjectAnimator;
Mady Mellor4b80b102016-01-22 08:03:58 -080024import android.animation.ValueAnimator.AnimatorUpdateListener;
Selim Cinekddf1b392016-05-27 16:33:10 -070025import android.annotation.Nullable;
Chris Wren51c75102013-07-16 20:49:17 -040026import android.content.Context;
Anthony Chen7acbb772017-04-07 16:45:25 -070027import android.content.res.Resources;
Mady Mellor9d03a522017-04-04 18:45:30 -070028import android.content.res.Configuration;
Selim Cinekcab4a602014-09-03 14:47:57 +020029import android.graphics.drawable.AnimatedVectorDrawable;
30import android.graphics.drawable.AnimationDrawable;
Selim Cinekb5605e52015-02-20 18:21:41 +010031import android.graphics.drawable.ColorDrawable;
Selim Cinekcab4a602014-09-03 14:47:57 +020032import android.graphics.drawable.Drawable;
Selim Cinekda42d652015-12-04 15:51:16 -080033import android.os.Build;
Selim Cineke9bad242016-06-15 11:46:37 -070034import android.os.Bundle;
Jorim Jaggib1cd3c12014-09-08 19:55:17 +020035import android.service.notification.StatusBarNotification;
Chris Wren51c75102013-07-16 20:49:17 -040036import android.util.AttributeSet;
Mady Mellorb0a82462016-04-30 17:31:02 -070037import android.util.FloatProperty;
38import android.util.Property;
Selim Cinek01af3342016-02-09 19:25:31 -080039import android.view.LayoutInflater;
Selim Cinek6183d122016-01-14 18:48:41 -080040import android.view.MotionEvent;
Selim Cinekeaa29ca2015-11-23 13:51:13 -080041import android.view.NotificationHeaderView;
Dan Sandlera5e0f412014-01-23 15:11:54 -050042import android.view.View;
Selim Cinek875a3a12016-11-18 17:52:16 -080043import android.view.ViewGroup;
Jorim Jaggib1cd3c12014-09-08 19:55:17 +020044import android.view.ViewStub;
Jorim Jaggife40f7d2014-04-28 15:20:04 +020045import android.view.accessibility.AccessibilityEvent;
Selim Cineke9bad242016-06-15 11:46:37 -070046import android.view.accessibility.AccessibilityNodeInfo;
Selim Cinek98713a42015-09-21 15:47:20 +020047import android.widget.Chronometer;
Mady Mellor95d743c2017-01-10 12:05:27 -080048import android.widget.FrameLayout;
Selim Cinekcab4a602014-09-03 14:47:57 +020049import android.widget.ImageView;
Selim Cinek1a48bab2017-02-17 19:38:40 -080050import android.widget.RemoteViews;
Selim Cinekb5605e52015-02-20 18:21:41 +010051
Selim Cinek5ba22542017-04-20 15:16:10 -070052import com.android.internal.annotations.VisibleForTesting;
Chris Wren698b1702016-05-23 11:16:32 -040053import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010054import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Selim Cinek4bb59342016-04-08 19:29:35 -070055import com.android.internal.util.NotificationColorUtil;
Selim Cinek875a3a12016-11-18 17:52:16 -080056import com.android.internal.widget.CachingIconView;
Mady Mellor95d743c2017-01-10 12:05:27 -080057import com.android.systemui.Dependency;
Selim Cinek0242fbb2016-10-19 13:38:32 -070058import com.android.systemui.Interpolators;
Dan Sandlera5e0f412014-01-23 15:11:54 -050059import com.android.systemui.R;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070060import com.android.systemui.classifier.FalsingManager;
Mady Mellor95d743c2017-01-10 12:05:27 -080061import com.android.systemui.plugins.PluginListener;
62import com.android.systemui.plugins.PluginManager;
63import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
64import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
65import com.android.systemui.statusbar.NotificationGuts.GutsContent;
Selim Cinek5cf1d052017-06-01 17:36:46 -070066import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
67import com.android.systemui.statusbar.notification.AboveShelfObserver;
Selim Cinekc897bd32016-03-18 17:32:31 -070068import com.android.systemui.statusbar.notification.HybridNotificationView;
Selim Cinek1a48bab2017-02-17 19:38:40 -080069import com.android.systemui.statusbar.notification.NotificationInflater;
Selim Cinek6743c0b2017-01-18 18:24:01 -080070import com.android.systemui.statusbar.notification.NotificationUtils;
Selim Cineka7d4f822016-12-06 14:34:47 -080071import com.android.systemui.statusbar.notification.VisualStabilityManager;
Selim Cinekb5605e52015-02-20 18:21:41 +010072import com.android.systemui.statusbar.phone.NotificationGroupManager;
Jason Monk2a6ea9c2017-01-26 11:14:51 -050073import com.android.systemui.statusbar.phone.StatusBar;
Selim Cinek31aada42015-12-18 17:51:15 -080074import com.android.systemui.statusbar.policy.HeadsUpManager;
Selim Cinek0cfbef42016-11-09 19:06:36 -080075import com.android.systemui.statusbar.stack.AnimationProperties;
76import com.android.systemui.statusbar.stack.ExpandableViewState;
Selim Cinekb5605e52015-02-20 18:21:41 +010077import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
Selim Cineke9bad242016-06-15 11:46:37 -070078import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
Selim Cinekb5605e52015-02-20 18:21:41 +010079import com.android.systemui.statusbar.stack.StackScrollState;
Selim Cinekb5605e52015-02-20 18:21:41 +010080
Mady Mellor4b80b102016-01-22 08:03:58 -080081import java.util.ArrayList;
Selim Cinekb5605e52015-02-20 18:21:41 +010082import java.util.List;
Dan Sandlera5e0f412014-01-23 15:11:54 -050083
Mady Mellor95d743c2017-01-10 12:05:27 -080084public class ExpandableNotificationRow extends ActivatableNotificationView
85 implements PluginListener<NotificationMenuRowPlugin> {
Selim Cinekb5605e52015-02-20 18:21:41 +010086
87 private static final int DEFAULT_DIVIDER_ALPHA = 0x29;
88 private static final int COLORED_DIVIDER_ALPHA = 0x7B;
Mady Mellor95d743c2017-01-10 12:05:27 -080089 private static final int MENU_VIEW_INDEX = 0;
90
Mady Mellor035badd2017-04-04 18:45:30 -070091 public interface LayoutListener {
92 public void onLayout();
93 }
94
95 private LayoutListener mLayoutListener;
Selim Cinek55a3e732017-05-25 18:30:10 -070096 private boolean mLowPriorityStateUpdated;
Selim Cinek1a48bab2017-02-17 19:38:40 -080097 private final NotificationInflater mNotificationInflater;
Selim Cinek0242fbb2016-10-19 13:38:32 -070098 private int mIconTransformContentShift;
Selim Cinek875a3a12016-11-18 17:52:16 -080099 private int mIconTransformContentShiftNoIcon;
Selim Cinek01af3342016-02-09 19:25:31 -0800100 private int mNotificationMinHeightLegacy;
101 private int mMaxHeadsUpHeightLegacy;
102 private int mMaxHeadsUpHeight;
Selim Cinek87ed69b2017-02-09 15:59:43 -0800103 private int mMaxHeadsUpHeightIncreased;
Selim Cinek01af3342016-02-09 19:25:31 -0800104 private int mNotificationMinHeight;
Selim Cinek7d1009b2017-01-25 15:28:28 -0800105 private int mNotificationMinHeightLarge;
Selim Cinek01af3342016-02-09 19:25:31 -0800106 private int mNotificationMaxHeight;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800107 private int mNotificationAmbientHeight;
Mady Mellorb0a82462016-04-30 17:31:02 -0700108 private int mIncreasedPaddingBetweenElements;
Chris Wren51c75102013-07-16 20:49:17 -0400109
Selim Cinek1685e632014-04-08 02:27:49 +0200110 /** Does this row contain layouts that can adapt to row expansion */
Chris Wren51c75102013-07-16 20:49:17 -0400111 private boolean mExpandable;
Selim Cinek1685e632014-04-08 02:27:49 +0200112 /** Has the user actively changed the expansion state of this row */
113 private boolean mHasUserChangedExpansion;
114 /** If {@link #mHasUserChangedExpansion}, has the user expanded this row */
Chris Wren51c75102013-07-16 20:49:17 -0400115 private boolean mUserExpanded;
Selim Cinek31aada42015-12-18 17:51:15 -0800116
117 /**
118 * Has this notification been expanded while it was pinned
119 */
120 private boolean mExpandedWhenPinned;
Selim Cinek1685e632014-04-08 02:27:49 +0200121 /** Is the user touching this row */
Chris Wren51c75102013-07-16 20:49:17 -0400122 private boolean mUserLocked;
Selim Cinek1685e632014-04-08 02:27:49 +0200123 /** Are we showing the "public" version */
Dan Sandlera5e0f412014-01-23 15:11:54 -0500124 private boolean mShowingPublic;
Jorim Jaggiae441282014-08-01 02:45:18 +0200125 private boolean mSensitive;
Selim Cinek3c76d502016-02-19 15:16:33 -0800126 private boolean mSensitiveHiddenInGeneral;
Jorim Jaggiae441282014-08-01 02:45:18 +0200127 private boolean mShowingPublicInitialized;
Selim Cinek60122be2015-04-15 18:16:50 -0700128 private boolean mHideSensitiveForIntrinsicHeight;
Chris Wren51c75102013-07-16 20:49:17 -0400129
Selim Cinek1685e632014-04-08 02:27:49 +0200130 /**
131 * Is this notification expanded by the system. The expansion state can be overridden by the
132 * user expansion.
133 */
134 private boolean mIsSystemExpanded;
Jorim Jaggidce3c4c2014-04-29 23:12:24 +0200135
136 /**
Selim Cinek83bc7832015-10-22 13:26:54 -0700137 * Whether the notification is on the keyguard and the expansion is disabled.
Jorim Jaggidce3c4c2014-04-29 23:12:24 +0200138 */
Selim Cinek83bc7832015-10-22 13:26:54 -0700139 private boolean mOnKeyguard;
Jorim Jaggidce3c4c2014-04-29 23:12:24 +0200140
Mady Mellorb0a82462016-04-30 17:31:02 -0700141 private Animator mTranslateAnim;
Mady Mellor4b80b102016-01-22 08:03:58 -0800142 private ArrayList<View> mTranslateableViews;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200143 private NotificationContentView mPublicLayout;
144 private NotificationContentView mPrivateLayout;
Adrian Rooseb434ff2017-01-11 11:18:48 -0800145 private NotificationContentView[] mLayouts;
Selim Cinek1685e632014-04-08 02:27:49 +0200146 private int mMaxExpandHeight;
Selim Cinek8d490d42015-04-10 00:05:50 -0700147 private int mHeadsUpHeight;
Selim Cinek4bb59342016-04-08 19:29:35 -0700148 private int mNotificationColor;
Chris Wren78403d72014-07-28 10:23:24 +0100149 private ExpansionLogger mLogger;
150 private String mLoggingKey;
Selim Cinek8d490d42015-04-10 00:05:50 -0700151 private NotificationGuts mGuts;
Selim Cinekda42d652015-12-04 15:51:16 -0800152 private NotificationData.Entry mEntry;
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200153 private StatusBarNotification mStatusBarNotification;
Mady Mellor3fd273e2016-03-15 21:08:14 -0700154 private String mAppName;
Selim Cinek1a521f32014-11-03 17:39:29 +0100155 private boolean mIsHeadsUp;
Selim Cinek98713a42015-09-21 15:47:20 +0200156 private boolean mLastChronometerRunning = true;
Selim Cinekb5605e52015-02-20 18:21:41 +0100157 private ViewStub mChildrenContainerStub;
158 private NotificationGroupManager mGroupManager;
Selim Cinekb5605e52015-02-20 18:21:41 +0100159 private boolean mChildrenExpanded;
Selim Cinek263398f2015-10-21 17:40:23 -0700160 private boolean mIsSummaryWithChildren;
Selim Cinekb5605e52015-02-20 18:21:41 +0100161 private NotificationChildrenContainer mChildrenContainer;
Mady Mellor95d743c2017-01-10 12:05:27 -0800162 private NotificationMenuRowPlugin mMenuRow;
Selim Cinekab29aeb2015-02-20 18:18:32 +0100163 private ViewStub mGutsStub;
Selim Cinekb5605e52015-02-20 18:21:41 +0100164 private boolean mIsSystemChildExpanded;
Selim Cinek684a4422015-04-15 16:18:39 -0700165 private boolean mIsPinned;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700166 private FalsingManager mFalsingManager;
Selim Cinek5cf1d052017-06-01 17:36:46 -0700167 private AboveShelfChangedListener mAboveShelfChangedListener;
Selim Cinek31aada42015-12-18 17:51:15 -0800168 private HeadsUpManager mHeadsUpManager;
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200169
Jorim Jaggi5eb67c22015-08-19 19:50:49 -0700170 private boolean mJustClicked;
Selim Cinek5a175d92015-11-23 18:01:33 -0800171 private boolean mIconAnimationRunning;
Selim Cinek34d93b02015-10-22 12:30:38 -0700172 private boolean mShowNoBackground;
Selim Cinek388df6d2015-10-22 13:25:11 -0700173 private ExpandableNotificationRow mNotificationParent;
Selim Cinek570981d2015-12-01 11:37:01 -0800174 private OnExpandClickListener mOnExpandClickListener;
Mady Mellorb0a82462016-04-30 17:31:02 -0700175 private boolean mGroupExpansionChanging;
176
Anthony Chen6bf88a02017-04-10 14:41:44 -0700177 /**
178 * Whether or not a notification that is not part of a group of notifications can be manually
179 * expanded by the user.
180 */
181 private boolean mEnableNonGroupedNotificationExpand;
182
183 /**
184 * Whether or not to update the background of the header of the notification when its expanded.
185 * If {@code true}, the header background will disappear when expanded.
186 */
187 private boolean mShowGroupBackgroundWhenExpanded;
188
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800189 private OnClickListener mExpandClickListener = new OnClickListener() {
190 @Override
191 public void onClick(View v) {
Selim Cinek414ad332017-02-24 19:06:12 -0800192 if (!mShowingPublic && (!mIsLowPriority || isExpanded())
193 && mGroupManager.isSummaryOfGroup(mStatusBarNotification)) {
Mady Mellor1a5d8ea2016-06-09 10:42:42 -0700194 mGroupExpansionChanging = true;
Chris Wren698b1702016-05-23 11:16:32 -0400195 final boolean wasExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
196 boolean nowExpanded = mGroupManager.toggleGroupExpansion(mStatusBarNotification);
197 mOnExpandClickListener.onExpandClicked(mEntry, nowExpanded);
Chris Wren698b1702016-05-23 11:16:32 -0400198 MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTIFICATION_GROUP_EXPANDER,
199 nowExpanded);
Selim Cinek414ad332017-02-24 19:06:12 -0800200 onExpansionChanged(true /* userAction */, wasExpanded);
Anthony Chen6bf88a02017-04-10 14:41:44 -0700201 } else if (mEnableNonGroupedNotificationExpand) {
Selim Cineke9bad242016-06-15 11:46:37 -0700202 if (v.isAccessibilityFocused()) {
203 mPrivateLayout.setFocusOnVisibilityChange();
204 }
Selim Cinek31aada42015-12-18 17:51:15 -0800205 boolean nowExpanded;
206 if (isPinned()) {
207 nowExpanded = !mExpandedWhenPinned;
208 mExpandedWhenPinned = nowExpanded;
209 } else {
210 nowExpanded = !isExpanded();
211 setUserExpanded(nowExpanded);
212 }
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800213 notifyHeightChanged(true);
Selim Cinek31aada42015-12-18 17:51:15 -0800214 mOnExpandClickListener.onExpandClicked(mEntry, nowExpanded);
Chris Wren698b1702016-05-23 11:16:32 -0400215 MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTIFICATION_EXPANDER,
216 nowExpanded);
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800217 }
218 }
219 };
Selim Cinek1b2a05e2016-04-28 14:20:39 -0700220 private boolean mForceUnlocked;
Selim Cinek3f19f602016-05-02 18:01:56 -0700221 private boolean mDismissed;
222 private boolean mKeepInParent;
223 private boolean mRemoved;
Mady Mellorb0a82462016-04-30 17:31:02 -0700224 private static final Property<ExpandableNotificationRow, Float> TRANSLATE_CONTENT =
225 new FloatProperty<ExpandableNotificationRow>("translate") {
226 @Override
227 public void setValue(ExpandableNotificationRow object, float value) {
228 object.setTranslation(value);
229 }
230
231 @Override
232 public Float get(ExpandableNotificationRow object) {
233 return object.getTranslation();
234 }
235 };
Selim Cinekddf1b392016-05-27 16:33:10 -0700236 private OnClickListener mOnClickListener;
Selim Cinek73cf02a2016-06-17 13:08:00 -0700237 private boolean mHeadsupDisappearRunning;
Selim Cineke9bad242016-06-15 11:46:37 -0700238 private View mChildAfterViewWhenDismissed;
239 private View mGroupParentWhenDismissed;
240 private boolean mRefocusOnDismiss;
Selim Cinek2b549f42016-11-22 16:38:51 -0800241 private float mContentTransformationAmount;
Selim Cinek0242fbb2016-10-19 13:38:32 -0700242 private boolean mIconsVisible = true;
Selim Cinekd127d792016-11-01 19:11:41 -0700243 private boolean mAboveShelf;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800244 private boolean mShowAmbient;
Selim Cinek875a3a12016-11-18 17:52:16 -0800245 private boolean mIsLastChild;
Selim Cineke9079112016-12-14 14:41:01 -0800246 private Runnable mOnDismissRunnable;
Selim Cinek6743c0b2017-01-18 18:24:01 -0800247 private boolean mIsLowPriority;
Selim Cineka7ed2c12017-01-23 20:47:24 -0800248 private boolean mIsColorized;
Selim Cinek7d1009b2017-01-25 15:28:28 -0800249 private boolean mUseIncreasedCollapsedHeight;
Selim Cinek87ed69b2017-02-09 15:59:43 -0800250 private boolean mUseIncreasedHeadsUpHeight;
Selim Cinekef8c2252017-02-10 14:52:18 -0800251 private float mTranslationWhenRemoved;
252 private boolean mWasChildInGroupWhenRemoved;
Adrian Roos6f6e1592017-05-02 16:22:53 -0700253 private int mNotificationColorAmbient;
Mady Mellorb0a82462016-04-30 17:31:02 -0700254
Mady Mellor43c2cd12016-12-12 21:05:13 -0800255 @Override
Mady Mellorb0a82462016-04-30 17:31:02 -0700256 public boolean isGroupExpansionChanging() {
257 if (isChildInGroup()) {
258 return mNotificationParent.isGroupExpansionChanging();
259 }
260 return mGroupExpansionChanging;
261 }
262
263 public void setGroupExpansionChanging(boolean changing) {
264 mGroupExpansionChanging = changing;
265 }
Jorim Jaggi5eb67c22015-08-19 19:50:49 -0700266
Adrian Roos599be342016-06-13 14:54:39 -0700267 @Override
268 public void setActualHeightAnimating(boolean animating) {
269 if (mPrivateLayout != null) {
270 mPrivateLayout.setContentHeightAnimating(animating);
271 }
272 }
273
Selim Cinek8d490d42015-04-10 00:05:50 -0700274 public NotificationContentView getPrivateLayout() {
275 return mPrivateLayout;
276 }
277
278 public NotificationContentView getPublicLayout() {
279 return mPublicLayout;
280 }
281
Selim Cinekcab4a602014-09-03 14:47:57 +0200282 public void setIconAnimationRunning(boolean running) {
Adrian Rooseb434ff2017-01-11 11:18:48 -0800283 for (NotificationContentView l : mLayouts) {
284 setIconAnimationRunning(running, l);
285 }
Selim Cinek5a175d92015-11-23 18:01:33 -0800286 if (mIsSummaryWithChildren) {
Mady Mellorb0a82462016-04-30 17:31:02 -0700287 setIconAnimationRunningForChild(running, mChildrenContainer.getHeaderView());
Selim Cinek414ad332017-02-24 19:06:12 -0800288 setIconAnimationRunningForChild(running, mChildrenContainer.getLowPriorityHeaderView());
Selim Cinek5a175d92015-11-23 18:01:33 -0800289 List<ExpandableNotificationRow> notificationChildren =
290 mChildrenContainer.getNotificationChildren();
291 for (int i = 0; i < notificationChildren.size(); i++) {
292 ExpandableNotificationRow child = notificationChildren.get(i);
293 child.setIconAnimationRunning(running);
294 }
295 }
296 mIconAnimationRunning = running;
Selim Cinekcab4a602014-09-03 14:47:57 +0200297 }
298
299 private void setIconAnimationRunning(boolean running, NotificationContentView layout) {
300 if (layout != null) {
301 View contractedChild = layout.getContractedChild();
302 View expandedChild = layout.getExpandedChild();
Selim Cinek8d490d42015-04-10 00:05:50 -0700303 View headsUpChild = layout.getHeadsUpChild();
Selim Cinekcab4a602014-09-03 14:47:57 +0200304 setIconAnimationRunningForChild(running, contractedChild);
305 setIconAnimationRunningForChild(running, expandedChild);
Selim Cinek8d490d42015-04-10 00:05:50 -0700306 setIconAnimationRunningForChild(running, headsUpChild);
Selim Cinekcab4a602014-09-03 14:47:57 +0200307 }
308 }
309
310 private void setIconAnimationRunningForChild(boolean running, View child) {
311 if (child != null) {
312 ImageView icon = (ImageView) child.findViewById(com.android.internal.R.id.icon);
313 setIconRunning(icon, running);
314 ImageView rightIcon = (ImageView) child.findViewById(
315 com.android.internal.R.id.right_icon);
316 setIconRunning(rightIcon, running);
317 }
318 }
319
320 private void setIconRunning(ImageView imageView, boolean running) {
321 if (imageView != null) {
322 Drawable drawable = imageView.getDrawable();
323 if (drawable instanceof AnimationDrawable) {
324 AnimationDrawable animationDrawable = (AnimationDrawable) drawable;
325 if (running) {
326 animationDrawable.start();
327 } else {
328 animationDrawable.stop();
329 }
330 } else if (drawable instanceof AnimatedVectorDrawable) {
331 AnimatedVectorDrawable animationDrawable = (AnimatedVectorDrawable) drawable;
332 if (running) {
333 animationDrawable.start();
334 } else {
335 animationDrawable.stop();
336 }
337 }
338 }
339 }
340
Selim Cinek5ba22542017-04-20 15:16:10 -0700341 public void updateNotification(NotificationData.Entry entry) {
Selim Cinekda42d652015-12-04 15:51:16 -0800342 mEntry = entry;
343 mStatusBarNotification = entry.notification;
Selim Cinek1a48bab2017-02-17 19:38:40 -0800344 mNotificationInflater.inflateNotificationViews();
Selim Cinekc478f902017-02-22 20:55:44 -0800345 }
346
Selim Cinek5ba22542017-04-20 15:16:10 -0700347 public void onNotificationUpdated() {
Adrian Rooseb434ff2017-01-11 11:18:48 -0800348 for (NotificationContentView l : mLayouts) {
Selim Cinekc478f902017-02-22 20:55:44 -0800349 l.onNotificationUpdated(mEntry);
Adrian Rooseb434ff2017-01-11 11:18:48 -0800350 }
Selim Cineka7ed2c12017-01-23 20:47:24 -0800351 mIsColorized = mStatusBarNotification.getNotification().isColorized();
Selim Cinek757d8792016-01-28 16:21:08 -0800352 mShowingPublicInitialized = false;
Selim Cinek4bb59342016-04-08 19:29:35 -0700353 updateNotificationColor();
Mady Mellor4c197602017-04-10 17:57:52 -0700354 if (mMenuRow != null) {
Mady Mellor4ab28202017-06-06 11:42:50 -0700355 mMenuRow.onNotificationUpdated(mStatusBarNotification);
Mady Mellor4c197602017-04-10 17:57:52 -0700356 }
Selim Cinek8fc93c92015-11-23 17:48:07 -0800357 if (mIsSummaryWithChildren) {
Selim Cinek414ad332017-02-24 19:06:12 -0800358 mChildrenContainer.recreateNotificationHeader(mExpandClickListener);
Selim Cinekc897bd32016-03-18 17:32:31 -0700359 mChildrenContainer.onNotificationUpdated();
Selim Cinek8fc93c92015-11-23 17:48:07 -0800360 }
Selim Cinek5a175d92015-11-23 18:01:33 -0800361 if (mIconAnimationRunning) {
362 setIconAnimationRunning(true);
363 }
Selim Cinekea4bef72015-12-02 15:51:10 -0800364 if (mNotificationParent != null) {
365 mNotificationParent.updateChildrenHeaderAppearance();
366 }
Selim Cinek263398f2015-10-21 17:40:23 -0700367 onChildrenCountChanged();
Selim Cinek624c02db2015-12-14 21:00:02 -0800368 // The public layouts expand button is always visible
369 mPublicLayout.updateExpandButtons(true);
Selim Cinekda42d652015-12-04 15:51:16 -0800370 updateLimits();
Selim Cinek0242fbb2016-10-19 13:38:32 -0700371 updateIconVisibilities();
Selim Cinek6743c0b2017-01-18 18:24:01 -0800372 updateShelfIconColor();
373 }
374
Lucas Dupinb6ed63b2017-05-30 16:17:42 -0700375 @VisibleForTesting
376 void updateShelfIconColor() {
Selim Cinek6743c0b2017-01-18 18:24:01 -0800377 StatusBarIconView expandedIcon = mEntry.expandedIcon;
378 boolean isPreL = Boolean.TRUE.equals(expandedIcon.getTag(R.id.icon_is_pre_L));
379 boolean colorize = !isPreL || NotificationUtils.isGrayscale(expandedIcon,
380 NotificationColorUtil.getInstance(mContext));
Selim Cinek875ba9b2017-02-13 16:20:17 -0800381 int color = StatusBarIconView.NO_COLOR;
Selim Cinek6743c0b2017-01-18 18:24:01 -0800382 if (colorize) {
Lucas Dupinb6ed63b2017-05-30 16:17:42 -0700383 NotificationHeaderView header = getVisibleNotificationHeader();
384 if (header != null) {
385 color = header.getOriginalIconColor();
386 } else {
387 color = mEntry.getContrastedColor(mContext, mIsLowPriority && !isExpanded(),
388 getBackgroundColorWithoutTint());
389 }
Selim Cinek6743c0b2017-01-18 18:24:01 -0800390 }
Selim Cinek875ba9b2017-02-13 16:20:17 -0800391 expandedIcon.setStaticDrawableColor(color);
Selim Cinekda42d652015-12-04 15:51:16 -0800392 }
393
Selim Cinek5cf1d052017-06-01 17:36:46 -0700394 public void setAboveShelfChangedListener(AboveShelfChangedListener aboveShelfChangedListener) {
395 mAboveShelfChangedListener = aboveShelfChangedListener;
396 }
397
Selim Cinek4705f292017-04-24 22:18:48 -0700398 @Override
399 public boolean isDimmable() {
400 if (!getShowingLayout().isDimmable()) {
401 return false;
402 }
403 return super.isDimmable();
404 }
405
Selim Cinekda42d652015-12-04 15:51:16 -0800406 private void updateLimits() {
Adrian Rooseb434ff2017-01-11 11:18:48 -0800407 for (NotificationContentView l : mLayouts) {
408 updateLimitsForView(l);
409 }
Selim Cineka1744872016-03-11 15:36:06 -0800410 }
411
412 private void updateLimitsForView(NotificationContentView layout) {
413 boolean customView = layout.getContractedChild().getId()
Selim Cinekda42d652015-12-04 15:51:16 -0800414 != com.android.internal.R.id.status_bar_latest_event_content;
415 boolean beforeN = mEntry.targetSdk < Build.VERSION_CODES.N;
Selim Cinek7d1009b2017-01-25 15:28:28 -0800416 int minHeight;
417 if (customView && beforeN && !mIsSummaryWithChildren) {
418 minHeight = mNotificationMinHeightLegacy;
419 } else if (mUseIncreasedCollapsedHeight && layout == mPrivateLayout) {
420 minHeight = mNotificationMinHeightLarge;
421 } else {
422 minHeight = mNotificationMinHeight;
423 }
Selim Cineka1744872016-03-11 15:36:06 -0800424 boolean headsUpCustom = layout.getHeadsUpChild() != null &&
425 layout.getHeadsUpChild().getId()
426 != com.android.internal.R.id.status_bar_latest_event_content;
Selim Cinek87ed69b2017-02-09 15:59:43 -0800427 int headsUpheight;
428 if (headsUpCustom && beforeN) {
429 headsUpheight = mMaxHeadsUpHeightLegacy;
430 } else if (mUseIncreasedHeadsUpHeight && layout == mPrivateLayout) {
431 headsUpheight = mMaxHeadsUpHeightIncreased;
432 } else {
433 headsUpheight = mMaxHeadsUpHeight;
434 }
Adrian Roos0aac04f2016-12-08 15:59:29 -0800435 layout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight,
436 mNotificationAmbientHeight);
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200437 }
438
439 public StatusBarNotification getStatusBarNotification() {
440 return mStatusBarNotification;
441 }
442
Selim Cinek281c2022016-10-13 19:14:43 -0700443 public NotificationData.Entry getEntry() {
444 return mEntry;
445 }
446
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700447 public boolean isHeadsUp() {
448 return mIsHeadsUp;
449 }
450
Selim Cinek1a521f32014-11-03 17:39:29 +0100451 public void setHeadsUp(boolean isHeadsUp) {
Selim Cinek5cf1d052017-06-01 17:36:46 -0700452 boolean wasAboveShelf = isAboveShelf();
Selim Cinekc80fdb12015-04-13 15:09:08 -0700453 int intrinsicBefore = getIntrinsicHeight();
Selim Cinek1a521f32014-11-03 17:39:29 +0100454 mIsHeadsUp = isHeadsUp;
Selim Cinek8d490d42015-04-10 00:05:50 -0700455 mPrivateLayout.setHeadsUp(isHeadsUp);
Selim Cinekb41b2f62016-04-26 14:03:29 -0700456 if (mIsSummaryWithChildren) {
457 // The overflow might change since we allow more lines as HUN.
458 mChildrenContainer.updateGroupOverflow();
459 }
Selim Cinekc80fdb12015-04-13 15:09:08 -0700460 if (intrinsicBefore != getIntrinsicHeight()) {
461 notifyHeightChanged(false /* needsAnimation */);
462 }
Selim Cinekd127d792016-11-01 19:11:41 -0700463 if (isHeadsUp) {
464 setAboveShelf(true);
Selim Cinek5cf1d052017-06-01 17:36:46 -0700465 } else if (isAboveShelf() != wasAboveShelf) {
466 mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
Selim Cinekd127d792016-11-01 19:11:41 -0700467 }
Selim Cinek1a521f32014-11-03 17:39:29 +0100468 }
469
Selim Cinekb5605e52015-02-20 18:21:41 +0100470 public void setGroupManager(NotificationGroupManager groupManager) {
471 mGroupManager = groupManager;
Selim Cinek83bc7832015-10-22 13:26:54 -0700472 mPrivateLayout.setGroupManager(groupManager);
Selim Cinekb5605e52015-02-20 18:21:41 +0100473 }
474
Adrian Roosb88b1a12015-12-09 18:51:05 -0800475 public void setRemoteInputController(RemoteInputController r) {
476 mPrivateLayout.setRemoteInputController(r);
477 }
478
Mady Mellor3fd273e2016-03-15 21:08:14 -0700479 public void setAppName(String appName) {
480 mAppName = appName;
Mady Mellor95d743c2017-01-10 12:05:27 -0800481 if (mMenuRow != null && mMenuRow.getMenuView() != null) {
Mady Mellor761cde12017-01-10 11:36:39 -0800482 mMenuRow.setAppName(mAppName);
Mady Mellor3fd273e2016-03-15 21:08:14 -0700483 }
484 }
485
Selim Cinekb5605e52015-02-20 18:21:41 +0100486 public void addChildNotification(ExpandableNotificationRow row) {
487 addChildNotification(row, -1);
488 }
489
490 /**
491 * Add a child notification to this view.
492 *
493 * @param row the row to add
494 * @param childIndex the index to add it at, if -1 it will be added at the end
495 */
496 public void addChildNotification(ExpandableNotificationRow row, int childIndex) {
497 if (mChildrenContainer == null) {
498 mChildrenContainerStub.inflate();
499 }
500 mChildrenContainer.addNotification(row, childIndex);
Selim Cinek263398f2015-10-21 17:40:23 -0700501 onChildrenCountChanged();
502 row.setIsChildInGroup(true, this);
Selim Cinekb5605e52015-02-20 18:21:41 +0100503 }
504
505 public void removeChildNotification(ExpandableNotificationRow row) {
506 if (mChildrenContainer != null) {
507 mChildrenContainer.removeNotification(row);
508 }
Selim Cinek263398f2015-10-21 17:40:23 -0700509 onChildrenCountChanged();
510 row.setIsChildInGroup(false, null);
511 }
512
Mady Mellor43c2cd12016-12-12 21:05:13 -0800513 @Override
Selim Cinek263398f2015-10-21 17:40:23 -0700514 public boolean isChildInGroup() {
Selim Cineka6c6bfb2015-10-29 16:27:08 -0700515 return mNotificationParent != null;
Selim Cinek263398f2015-10-21 17:40:23 -0700516 }
517
Selim Cinek388df6d2015-10-22 13:25:11 -0700518 public ExpandableNotificationRow getNotificationParent() {
519 return mNotificationParent;
520 }
521
Selim Cinek263398f2015-10-21 17:40:23 -0700522 /**
523 * @param isChildInGroup Is this notification now in a group
524 * @param parent the new parent notification
525 */
Selim Cineka6c6bfb2015-10-29 16:27:08 -0700526 public void setIsChildInGroup(boolean isChildInGroup, ExpandableNotificationRow parent) {;
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500527 boolean childInGroup = StatusBar.ENABLE_CHILD_NOTIFICATIONS && isChildInGroup;
Selim Cineka6c6bfb2015-10-29 16:27:08 -0700528 mNotificationParent = childInGroup ? parent : null;
529 mPrivateLayout.setIsChildInGroup(childInGroup);
Selim Cinek5ba22542017-04-20 15:16:10 -0700530 mNotificationInflater.setIsChildInGroup(childInGroup);
Mady Mellorc7d65b42016-05-04 11:44:57 -0400531 resetBackgroundAlpha();
Mady Mellorb0a82462016-04-30 17:31:02 -0700532 updateBackgroundForGroupState();
Selim Cinekddf1b392016-05-27 16:33:10 -0700533 updateClickAndFocus();
Mady Mellorb0a82462016-04-30 17:31:02 -0700534 if (mNotificationParent != null) {
Selim Cinek9ce32852017-02-15 16:21:10 -0800535 setOverrideTintColor(NO_COLOR, 0.0f);
Mady Mellorb0a82462016-04-30 17:31:02 -0700536 mNotificationParent.updateBackgroundForGroupState();
537 }
Selim Cinekdb167372016-11-17 15:41:17 -0800538 updateIconVisibilities();
Selim Cinek34d93b02015-10-22 12:30:38 -0700539 }
540
541 @Override
Selim Cinek72109472016-01-15 16:33:22 -0800542 public boolean onTouchEvent(MotionEvent event) {
543 if (event.getActionMasked() != MotionEvent.ACTION_DOWN
544 || !isChildInGroup() || isGroupExpanded()) {
545 return super.onTouchEvent(event);
546 } else {
547 return false;
548 }
549 }
550
551 @Override
Mady Mellorf0625802016-02-11 18:03:48 -0800552 protected boolean handleSlideBack() {
Mady Mellor95d743c2017-01-10 12:05:27 -0800553 if (mMenuRow != null && mMenuRow.isMenuVisible()) {
Mady Mellorf0625802016-02-11 18:03:48 -0800554 animateTranslateNotification(0 /* targetLeft */);
555 return true;
556 }
557 return false;
558 }
559
560 @Override
Selim Cinek34d93b02015-10-22 12:30:38 -0700561 protected boolean shouldHideBackground() {
562 return super.shouldHideBackground() || mShowNoBackground;
Selim Cinek263398f2015-10-21 17:40:23 -0700563 }
564
565 @Override
566 public boolean isSummaryWithChildren() {
567 return mIsSummaryWithChildren;
Selim Cinekb5605e52015-02-20 18:21:41 +0100568 }
569
570 @Override
571 public boolean areChildrenExpanded() {
572 return mChildrenExpanded;
573 }
574
575 public List<ExpandableNotificationRow> getNotificationChildren() {
576 return mChildrenContainer == null ? null : mChildrenContainer.getNotificationChildren();
577 }
578
Selim Cinekeef84282015-10-30 16:28:00 -0700579 public int getNumberOfNotificationChildren() {
580 if (mChildrenContainer == null) {
581 return 0;
582 }
583 return mChildrenContainer.getNotificationChildren().size();
584 }
585
Selim Cinekb5605e52015-02-20 18:21:41 +0100586 /**
587 * Apply the order given in the list to the children.
588 *
589 * @param childOrder the new list order
Selim Cineka7d4f822016-12-06 14:34:47 -0800590 * @param visualStabilityManager
591 * @param callback the callback to invoked in case it is not allowed
Selim Cinekb5605e52015-02-20 18:21:41 +0100592 * @return whether the list order has changed
593 */
Selim Cineka7d4f822016-12-06 14:34:47 -0800594 public boolean applyChildOrder(List<ExpandableNotificationRow> childOrder,
595 VisualStabilityManager visualStabilityManager,
596 VisualStabilityManager.Callback callback) {
597 return mChildrenContainer != null && mChildrenContainer.applyChildOrder(childOrder,
598 visualStabilityManager, callback);
Selim Cinekb5605e52015-02-20 18:21:41 +0100599 }
600
601 public void getChildrenStates(StackScrollState resultState) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700602 if (mIsSummaryWithChildren) {
Selim Cinekbbcebde2016-11-09 18:28:20 -0800603 ExpandableViewState parentState = resultState.getViewStateForView(this);
Selim Cinekb5605e52015-02-20 18:21:41 +0100604 mChildrenContainer.getState(resultState, parentState);
605 }
606 }
607
608 public void applyChildrenState(StackScrollState state) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700609 if (mIsSummaryWithChildren) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100610 mChildrenContainer.applyState(state);
611 }
612 }
613
614 public void prepareExpansionChanged(StackScrollState state) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700615 if (mIsSummaryWithChildren) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100616 mChildrenContainer.prepareExpansionChanged(state);
617 }
618 }
619
Selim Cinek0cfbef42016-11-09 19:06:36 -0800620 public void startChildAnimation(StackScrollState finalState, AnimationProperties properties) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700621 if (mIsSummaryWithChildren) {
Selim Cinek0cfbef42016-11-09 19:06:36 -0800622 mChildrenContainer.startAnimationToState(finalState, properties);
Selim Cinekb5605e52015-02-20 18:21:41 +0100623 }
624 }
625
626 public ExpandableNotificationRow getViewAtPosition(float y) {
Selim Cinek43d30f02016-03-04 10:51:32 -0800627 if (!mIsSummaryWithChildren || !mChildrenExpanded) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100628 return this;
629 } else {
630 ExpandableNotificationRow view = mChildrenContainer.getViewAtPosition(y);
631 return view == null ? this : view;
632 }
633 }
634
Selim Cinekab29aeb2015-02-20 18:18:32 +0100635 public NotificationGuts getGuts() {
636 return mGuts;
637 }
638
Selim Cinek684a4422015-04-15 16:18:39 -0700639 /**
640 * Set this notification to be pinned to the top if {@link #isHeadsUp()} is true. By doing this
641 * the notification will be rendered on top of the screen.
642 *
643 * @param pinned whether it is pinned
644 */
645 public void setPinned(boolean pinned) {
Selim Cinekdef35a82016-05-03 15:52:51 -0700646 int intrinsicHeight = getIntrinsicHeight();
Selim Cinek5cf1d052017-06-01 17:36:46 -0700647 boolean wasAboveShelf = isAboveShelf();
Selim Cinek684a4422015-04-15 16:18:39 -0700648 mIsPinned = pinned;
Selim Cinekdef35a82016-05-03 15:52:51 -0700649 if (intrinsicHeight != getIntrinsicHeight()) {
Selim Cinekbb42b7d2016-08-10 13:02:38 -0700650 notifyHeightChanged(false /* needsAnimation */);
Selim Cinekdef35a82016-05-03 15:52:51 -0700651 }
Selim Cinek31aada42015-12-18 17:51:15 -0800652 if (pinned) {
653 setIconAnimationRunning(true);
654 mExpandedWhenPinned = false;
655 } else if (mExpandedWhenPinned) {
656 setUserExpanded(true);
657 }
Selim Cinek98713a42015-09-21 15:47:20 +0200658 setChronometerRunning(mLastChronometerRunning);
Selim Cinek5cf1d052017-06-01 17:36:46 -0700659 if (isAboveShelf() != wasAboveShelf) {
660 mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
661 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700662 }
663
Selim Cinek684a4422015-04-15 16:18:39 -0700664 public boolean isPinned() {
665 return mIsPinned;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700666 }
667
Selim Cinekd127d792016-11-01 19:11:41 -0700668 @Override
669 public int getPinnedHeadsUpHeight() {
670 return getPinnedHeadsUpHeight(true /* atLeastMinHeight */);
671 }
672
Selim Cinek31aada42015-12-18 17:51:15 -0800673 /**
674 * @param atLeastMinHeight should the value returned be at least the minimum height.
675 * Used to avoid cyclic calls
676 * @return the height of the heads up notification when pinned
677 */
Selim Cinekd127d792016-11-01 19:11:41 -0700678 private int getPinnedHeadsUpHeight(boolean atLeastMinHeight) {
Selim Cinek77019c72015-12-09 10:18:02 -0800679 if (mIsSummaryWithChildren) {
680 return mChildrenContainer.getIntrinsicHeight();
681 }
Selim Cinek31aada42015-12-18 17:51:15 -0800682 if(mExpandedWhenPinned) {
683 return Math.max(getMaxExpandHeight(), mHeadsUpHeight);
684 } else if (atLeastMinHeight) {
Selim Cinek567e8452016-03-24 10:54:56 -0700685 return Math.max(getCollapsedHeight(), mHeadsUpHeight);
Selim Cinek31aada42015-12-18 17:51:15 -0800686 } else {
687 return mHeadsUpHeight;
688 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700689 }
690
Jorim Jaggi5eb67c22015-08-19 19:50:49 -0700691 /**
692 * Mark whether this notification was just clicked, i.e. the user has just clicked this
693 * notification in this frame.
694 */
695 public void setJustClicked(boolean justClicked) {
696 mJustClicked = justClicked;
697 }
698
699 /**
700 * @return true if this notification has been clicked in this frame, false otherwise
701 */
702 public boolean wasJustClicked() {
703 return mJustClicked;
704 }
705
Selim Cinek98713a42015-09-21 15:47:20 +0200706 public void setChronometerRunning(boolean running) {
707 mLastChronometerRunning = running;
708 setChronometerRunning(running, mPrivateLayout);
709 setChronometerRunning(running, mPublicLayout);
710 if (mChildrenContainer != null) {
711 List<ExpandableNotificationRow> notificationChildren =
712 mChildrenContainer.getNotificationChildren();
713 for (int i = 0; i < notificationChildren.size(); i++) {
714 ExpandableNotificationRow child = notificationChildren.get(i);
715 child.setChronometerRunning(running);
716 }
717 }
718 }
719
720 private void setChronometerRunning(boolean running, NotificationContentView layout) {
721 if (layout != null) {
722 running = running || isPinned();
723 View contractedChild = layout.getContractedChild();
724 View expandedChild = layout.getExpandedChild();
725 View headsUpChild = layout.getHeadsUpChild();
726 setChronometerRunningForChild(running, contractedChild);
727 setChronometerRunningForChild(running, expandedChild);
728 setChronometerRunningForChild(running, headsUpChild);
729 }
730 }
731
732 private void setChronometerRunningForChild(boolean running, View child) {
733 if (child != null) {
734 View chronometer = child.findViewById(com.android.internal.R.id.chronometer);
735 if (chronometer instanceof Chronometer) {
736 ((Chronometer) chronometer).setStarted(running);
737 }
738 }
739 }
740
Selim Cinekea4bef72015-12-02 15:51:10 -0800741 public NotificationHeaderView getNotificationHeader() {
Mady Mellorb0a82462016-04-30 17:31:02 -0700742 if (mIsSummaryWithChildren) {
743 return mChildrenContainer.getHeaderView();
Selim Cinek8d6440d2015-10-22 13:00:05 -0700744 }
Selim Cinekea4bef72015-12-02 15:51:10 -0800745 return mPrivateLayout.getNotificationHeader();
Selim Cinek8d6440d2015-10-22 13:00:05 -0700746 }
747
Selim Cinek414ad332017-02-24 19:06:12 -0800748 /**
749 * @return the currently visible notification header. This can be different from
750 * {@link #getNotificationHeader()} in case it is a low-priority group.
751 */
752 public NotificationHeaderView getVisibleNotificationHeader() {
Selim Cinekaa3901a2016-08-05 11:04:37 -0700753 if (mIsSummaryWithChildren && !mShowingPublic) {
Selim Cinek414ad332017-02-24 19:06:12 -0800754 return mChildrenContainer.getVisibleHeader();
Selim Cinek34eda5e2016-02-18 17:10:43 -0800755 }
756 return getShowingLayout().getVisibleNotificationHeader();
757 }
758
Selim Cinekb26afa32017-06-29 10:28:17 +0200759
760 /**
761 * @return the contracted notification header. This can be different from
762 * {@link #getNotificationHeader()} and also {@link #getVisibleNotificationHeader()} and only
763 * returns the contracted version.
764 */
765 public NotificationHeaderView getContractedNotificationHeader() {
766 if (mIsSummaryWithChildren) {
767 return mChildrenContainer.getHeaderView();
768 }
769 return mPrivateLayout.getContractedNotificationHeader();
770 }
771
Selim Cinek570981d2015-12-01 11:37:01 -0800772 public void setOnExpandClickListener(OnExpandClickListener onExpandClickListener) {
773 mOnExpandClickListener = onExpandClickListener;
774 }
775
Selim Cinekddf1b392016-05-27 16:33:10 -0700776 @Override
777 public void setOnClickListener(@Nullable OnClickListener l) {
778 super.setOnClickListener(l);
779 mOnClickListener = l;
780 updateClickAndFocus();
781 }
782
783 private void updateClickAndFocus() {
784 boolean normalChild = !isChildInGroup() || isGroupExpanded();
785 boolean clickable = mOnClickListener != null && normalChild;
786 if (isFocusable() != normalChild) {
787 setFocusable(normalChild);
788 }
789 if (isClickable() != clickable) {
790 setClickable(clickable);
791 }
792 }
793
Selim Cinek31aada42015-12-18 17:51:15 -0800794 public void setHeadsUpManager(HeadsUpManager headsUpManager) {
795 mHeadsUpManager = headsUpManager;
796 }
797
Mady Mellor87d79452017-01-10 11:52:52 -0800798 public void setGutsView(MenuItem item) {
Mady Mellor95d743c2017-01-10 12:05:27 -0800799 if (mGuts != null && item.getGutsView() instanceof GutsContent) {
800 ((GutsContent) item.getGutsView()).setGutsParent(mGuts);
801 mGuts.setGutsContent((GutsContent) item.getGutsView());
Mady Mellor87d79452017-01-10 11:52:52 -0800802 }
803 }
804
Mady Mellor95d743c2017-01-10 12:05:27 -0800805 @Override
806 protected void onAttachedToWindow() {
807 super.onAttachedToWindow();
808 Dependency.get(PluginManager.class).addPluginListener(this,
809 NotificationMenuRowPlugin.class, false /* Allow multiple */);
810 }
811
812 @Override
813 protected void onDetachedFromWindow() {
814 super.onDetachedFromWindow();
815 Dependency.get(PluginManager.class).removePluginListener(this);
816 }
817
818 @Override
819 public void onPluginConnected(NotificationMenuRowPlugin plugin, Context pluginContext) {
820 boolean existed = mMenuRow.getMenuView() != null;
821 if (existed) {
822 removeView(mMenuRow.getMenuView());
823 }
824 mMenuRow = plugin;
825 if (mMenuRow.useDefaultMenuItems()) {
Mady Mellor4c197602017-04-10 17:57:52 -0700826 ArrayList<MenuItem> items = new ArrayList<>();
827 items.add(NotificationMenuRow.createInfoItem(mContext));
828 items.add(NotificationMenuRow.createSnoozeItem(mContext));
829 mMenuRow.setMenuItems(items);
Mady Mellor95d743c2017-01-10 12:05:27 -0800830 }
831 if (existed) {
832 createMenu();
833 }
834 }
835
836 @Override
837 public void onPluginDisconnected(NotificationMenuRowPlugin plugin) {
838 boolean existed = mMenuRow.getMenuView() != null;
839 mMenuRow = new NotificationMenuRow(mContext); // Back to default
840 if (existed) {
841 createMenu();
842 }
843 }
844
845 public NotificationMenuRowPlugin createMenu() {
846 if (mMenuRow.getMenuView() == null) {
Mady Mellor4ab28202017-06-06 11:42:50 -0700847 mMenuRow.createMenu(this, mStatusBarNotification);
Mady Mellor95d743c2017-01-10 12:05:27 -0800848 mMenuRow.setAppName(mAppName);
849 FrameLayout.LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT,
850 LayoutParams.MATCH_PARENT);
851 addView(mMenuRow.getMenuView(), MENU_VIEW_INDEX, lp);
852 }
853 return mMenuRow;
854 }
855
Mady Mellor95d743c2017-01-10 12:05:27 -0800856 public NotificationMenuRowPlugin getProvider() {
857 return mMenuRow;
858 }
859
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700860 @Override
Selim Cinek1a48bab2017-02-17 19:38:40 -0800861 public void onDensityOrFontScaleChanged() {
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700862 super.onDensityOrFontScaleChanged();
Selim Cinek01af3342016-02-09 19:25:31 -0800863 initDimens();
Selim Cinek817abe72017-05-24 11:08:55 -0700864 // Let's update our childrencontainer. This is intentionally not guarded with
865 // mIsSummaryWithChildren since we might have had children but not anymore.
866 if (mChildrenContainer != null) {
867 mChildrenContainer.reInflateViews(mExpandClickListener, mEntry.notification);
Selim Cinek01af3342016-02-09 19:25:31 -0800868 }
869 if (mGuts != null) {
870 View oldGuts = mGuts;
871 int index = indexOfChild(oldGuts);
872 removeView(oldGuts);
873 mGuts = (NotificationGuts) LayoutInflater.from(mContext).inflate(
874 R.layout.notification_guts, this, false);
875 mGuts.setVisibility(oldGuts.getVisibility());
876 addView(mGuts, index);
877 }
Mady Mellor95d743c2017-01-10 12:05:27 -0800878 View oldMenu = mMenuRow.getMenuView();
879 if (oldMenu != null) {
Mady Mellor761cde12017-01-10 11:36:39 -0800880 int menuIndex = indexOfChild(oldMenu);
881 removeView(oldMenu);
Mady Mellor4ab28202017-06-06 11:42:50 -0700882 mMenuRow.createMenu(ExpandableNotificationRow.this, mStatusBarNotification);
Mady Mellor761cde12017-01-10 11:36:39 -0800883 mMenuRow.setAppName(mAppName);
Mady Mellor95d743c2017-01-10 12:05:27 -0800884 addView(mMenuRow.getMenuView(), menuIndex);
Mady Mellor4b80b102016-01-22 08:03:58 -0800885 }
Adrian Rooseb434ff2017-01-11 11:18:48 -0800886 for (NotificationContentView l : mLayouts) {
887 l.reInflateViews();
888 }
Selim Cinek1a48bab2017-02-17 19:38:40 -0800889 mNotificationInflater.onDensityOrFontScaleChanged();
890 onNotificationUpdated();
Selim Cinek01af3342016-02-09 19:25:31 -0800891 }
892
Mady Mellor9d03a522017-04-04 18:45:30 -0700893 @Override
894 public void onConfigurationChanged(Configuration newConfig) {
895 if (mMenuRow.getMenuView() != null) {
896 mMenuRow.onConfigurationChanged();
897 }
898 }
899
Selim Cinekc3179332016-03-04 14:44:56 -0800900 public void setContentBackground(int customBackgroundColor, boolean animate,
901 NotificationContentView notificationContentView) {
902 if (getShowingLayout() == notificationContentView) {
903 setTintColor(customBackgroundColor, animate);
904 }
905 }
906
Adrian Roos0bd8a4b2016-03-14 16:21:44 -0700907 public void closeRemoteInput() {
Adrian Rooseb434ff2017-01-11 11:18:48 -0800908 for (NotificationContentView l : mLayouts) {
909 l.closeRemoteInput();
910 }
Adrian Roos0bd8a4b2016-03-14 16:21:44 -0700911 }
912
Selim Cinekc897bd32016-03-18 17:32:31 -0700913 /**
914 * Set by how much the single line view should be indented.
915 */
916 public void setSingleLineWidthIndention(int indention) {
917 mPrivateLayout.setSingleLineWidthIndention(indention);
918 }
919
920 public int getNotificationColor() {
Selim Cinek4bb59342016-04-08 19:29:35 -0700921 return mNotificationColor;
922 }
923
924 private void updateNotificationColor() {
925 mNotificationColor = NotificationColorUtil.resolveContrastColor(mContext,
Selim Cinekac5f0272017-05-02 16:05:41 -0700926 getStatusBarNotification().getNotification().color,
927 getBackgroundColorWithoutTint());
Adrian Roos6f6e1592017-05-02 16:22:53 -0700928 mNotificationColorAmbient = NotificationColorUtil.resolveAmbientColor(mContext,
929 getStatusBarNotification().getNotification().color);
Selim Cinekc897bd32016-03-18 17:32:31 -0700930 }
931
932 public HybridNotificationView getSingleLineView() {
933 return mPrivateLayout.getSingleLineView();
934 }
935
Adrian Roos6f6e1592017-05-02 16:22:53 -0700936 public HybridNotificationView getAmbientSingleLineView() {
937 return getShowingLayout().getAmbientSingleLineChild();
938 }
939
Selim Cinekf07d0622016-03-21 19:52:52 -0700940 public boolean isOnKeyguard() {
941 return mOnKeyguard;
942 }
943
Selim Cinekc1e389d2016-04-07 11:02:57 -0700944 public void removeAllChildren() {
945 List<ExpandableNotificationRow> notificationChildren
946 = mChildrenContainer.getNotificationChildren();
947 ArrayList<ExpandableNotificationRow> clonedList = new ArrayList<>(notificationChildren);
948 for (int i = 0; i < clonedList.size(); i++) {
949 ExpandableNotificationRow row = clonedList.get(i);
Selim Cinek3f19f602016-05-02 18:01:56 -0700950 if (row.keepInParent()) {
951 continue;
952 }
Selim Cinekc1e389d2016-04-07 11:02:57 -0700953 mChildrenContainer.removeNotification(row);
Selim Cinekc1e389d2016-04-07 11:02:57 -0700954 row.setIsChildInGroup(false, null);
955 }
956 onChildrenCountChanged();
957 }
958
Selim Cinek1b2a05e2016-04-28 14:20:39 -0700959 public void setForceUnlocked(boolean forceUnlocked) {
960 mForceUnlocked = forceUnlocked;
961 if (mIsSummaryWithChildren) {
962 List<ExpandableNotificationRow> notificationChildren = getNotificationChildren();
963 for (ExpandableNotificationRow child : notificationChildren) {
964 child.setForceUnlocked(forceUnlocked);
965 }
966 }
967 }
968
Selim Cineke9bad242016-06-15 11:46:37 -0700969 public void setDismissed(boolean dismissed, boolean fromAccessibility) {
Selim Cinek3f19f602016-05-02 18:01:56 -0700970 mDismissed = dismissed;
Selim Cineke9bad242016-06-15 11:46:37 -0700971 mGroupParentWhenDismissed = mNotificationParent;
972 mRefocusOnDismiss = fromAccessibility;
973 mChildAfterViewWhenDismissed = null;
974 if (isChildInGroup()) {
975 List<ExpandableNotificationRow> notificationChildren =
976 mNotificationParent.getNotificationChildren();
977 int i = notificationChildren.indexOf(this);
978 if (i != -1 && i < notificationChildren.size() - 1) {
979 mChildAfterViewWhenDismissed = notificationChildren.get(i + 1);
980 }
981 }
Selim Cinek3f19f602016-05-02 18:01:56 -0700982 }
983
984 public boolean isDismissed() {
985 return mDismissed;
986 }
987
988 public boolean keepInParent() {
989 return mKeepInParent;
990 }
991
992 public void setKeepInParent(boolean keepInParent) {
993 mKeepInParent = keepInParent;
994 }
995
996 public boolean isRemoved() {
997 return mRemoved;
998 }
999
Adrian Roosd009ab12016-05-20 17:58:53 -07001000 public void setRemoved() {
1001 mRemoved = true;
Selim Cinekef8c2252017-02-10 14:52:18 -08001002 mTranslationWhenRemoved = getTranslationY();
1003 mWasChildInGroupWhenRemoved = isChildInGroup();
1004 if (isChildInGroup()) {
1005 mTranslationWhenRemoved += getNotificationParent().getTranslationY();
1006 }
Adrian Roosd009ab12016-05-20 17:58:53 -07001007 mPrivateLayout.setRemoved();
Selim Cinek3f19f602016-05-02 18:01:56 -07001008 }
1009
Selim Cinekef8c2252017-02-10 14:52:18 -08001010 public boolean wasChildInGroupWhenRemoved() {
1011 return mWasChildInGroupWhenRemoved;
1012 }
1013
1014 public float getTranslationWhenRemoved() {
1015 return mTranslationWhenRemoved;
1016 }
1017
Selim Cinekd1395642016-04-28 12:22:42 -07001018 public NotificationChildrenContainer getChildrenContainer() {
1019 return mChildrenContainer;
1020 }
1021
Selim Cinekcafa87f2016-10-26 17:00:17 -07001022 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
Selim Cinek5cf1d052017-06-01 17:36:46 -07001023 boolean wasAboveShelf = isAboveShelf();
Selim Cinekcafa87f2016-10-26 17:00:17 -07001024 mHeadsupDisappearRunning = headsUpAnimatingAway;
1025 mPrivateLayout.setHeadsUpAnimatingAway(headsUpAnimatingAway);
Selim Cinek5cf1d052017-06-01 17:36:46 -07001026 if (isAboveShelf() != wasAboveShelf) {
1027 mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
1028 }
Selim Cinekcafa87f2016-10-26 17:00:17 -07001029 }
1030
1031 /**
1032 * @return if the view was just heads upped and is now animating away. During such a time the
1033 * layout needs to be kept consistent
1034 */
1035 public boolean isHeadsUpAnimatingAway() {
1036 return mHeadsupDisappearRunning;
Selim Cinek73cf02a2016-06-17 13:08:00 -07001037 }
1038
Selim Cineke9bad242016-06-15 11:46:37 -07001039 public View getChildAfterViewWhenDismissed() {
1040 return mChildAfterViewWhenDismissed;
1041 }
1042
1043 public View getGroupParentWhenDismissed() {
1044 return mGroupParentWhenDismissed;
1045 }
1046
Selim Cinek9e624e72016-07-20 13:46:49 -07001047 public void performDismiss() {
Selim Cineke9079112016-12-14 14:41:01 -08001048 if (mOnDismissRunnable != null) {
1049 mOnDismissRunnable.run();
1050 }
Selim Cinek9e624e72016-07-20 13:46:49 -07001051 }
1052
Selim Cineke9079112016-12-14 14:41:01 -08001053 public void setOnDismissRunnable(Runnable onDismissRunnable) {
1054 mOnDismissRunnable = onDismissRunnable;
Selim Cinek9e624e72016-07-20 13:46:49 -07001055 }
1056
Selim Cinek281c2022016-10-13 19:14:43 -07001057 public View getNotificationIcon() {
Selim Cinek414ad332017-02-24 19:06:12 -08001058 NotificationHeaderView notificationHeader = getVisibleNotificationHeader();
Selim Cinek281c2022016-10-13 19:14:43 -07001059 if (notificationHeader != null) {
1060 return notificationHeader.getIcon();
1061 }
1062 return null;
1063 }
1064
1065 /**
1066 * @return whether the notification is currently showing a view with an icon.
1067 */
1068 public boolean isShowingIcon() {
Mady Mellor434180c2017-02-13 11:29:42 -08001069 if (areGutsExposed()) {
1070 return false;
1071 }
Selim Cinek2ffa02f2017-03-06 15:56:37 -08001072 return getVisibleNotificationHeader() != null;
Selim Cinek281c2022016-10-13 19:14:43 -07001073 }
1074
Selim Cinek0242fbb2016-10-19 13:38:32 -07001075 /**
1076 * Set how much this notification is transformed into an icon.
1077 *
Selim Cinek2b549f42016-11-22 16:38:51 -08001078 * @param contentTransformationAmount A value from 0 to 1 indicating how much we are transformed
1079 * to the content away
Selim Cinek875a3a12016-11-18 17:52:16 -08001080 * @param isLastChild is this the last child in the list. If true, then the transformation is
1081 * different since it's content fades out.
Selim Cinek0242fbb2016-10-19 13:38:32 -07001082 */
Selim Cinek2b549f42016-11-22 16:38:51 -08001083 public void setContentTransformationAmount(float contentTransformationAmount,
1084 boolean isLastChild) {
Selim Cinek875a3a12016-11-18 17:52:16 -08001085 boolean changeTransformation = isLastChild != mIsLastChild;
Selim Cinek2b549f42016-11-22 16:38:51 -08001086 changeTransformation |= mContentTransformationAmount != contentTransformationAmount;
Selim Cinek875a3a12016-11-18 17:52:16 -08001087 mIsLastChild = isLastChild;
Selim Cinek2b549f42016-11-22 16:38:51 -08001088 mContentTransformationAmount = contentTransformationAmount;
Selim Cinek875a3a12016-11-18 17:52:16 -08001089 if (changeTransformation) {
1090 updateContentTransformation();
Selim Cinek2b549f42016-11-22 16:38:51 -08001091 }
1092 }
1093
1094 /**
1095 * Set the icons to be visible of this notification.
1096 */
1097 public void setIconsVisible(boolean iconsVisible) {
1098 if (iconsVisible != mIconsVisible) {
1099 mIconsVisible = iconsVisible;
1100 updateIconVisibilities();
Selim Cinek0242fbb2016-10-19 13:38:32 -07001101 }
1102 }
1103
Selim Cinekdb167372016-11-17 15:41:17 -08001104 @Override
1105 protected void onBelowSpeedBumpChanged() {
1106 updateIconVisibilities();
1107 }
1108
Selim Cinek875a3a12016-11-18 17:52:16 -08001109 private void updateContentTransformation() {
1110 float contentAlpha;
Selim Cinek2b549f42016-11-22 16:38:51 -08001111 float translationY = -mContentTransformationAmount * mIconTransformContentShift;
Selim Cinek875a3a12016-11-18 17:52:16 -08001112 if (mIsLastChild) {
Selim Cinek2b549f42016-11-22 16:38:51 -08001113 contentAlpha = 1.0f - mContentTransformationAmount;
Selim Cinek875a3a12016-11-18 17:52:16 -08001114 contentAlpha = Math.min(contentAlpha / 0.5f, 1.0f);
Selim Cinek0242fbb2016-10-19 13:38:32 -07001115 contentAlpha = Interpolators.ALPHA_OUT.getInterpolation(contentAlpha);
Selim Cinek875a3a12016-11-18 17:52:16 -08001116 translationY *= 0.4f;
1117 } else {
1118 contentAlpha = 1.0f;
1119 }
Adrian Rooseb434ff2017-01-11 11:18:48 -08001120 for (NotificationContentView l : mLayouts) {
1121 l.setAlpha(contentAlpha);
1122 l.setTranslationY(translationY);
1123 }
Selim Cinek875a3a12016-11-18 17:52:16 -08001124 if (mChildrenContainer != null) {
1125 mChildrenContainer.setAlpha(contentAlpha);
1126 mChildrenContainer.setTranslationY(translationY);
1127 // TODO: handle children fade out better
Selim Cinek0242fbb2016-10-19 13:38:32 -07001128 }
1129 }
1130
1131 private void updateIconVisibilities() {
Selim Cinek17e1b692016-12-02 18:19:11 -08001132 boolean visible = isChildInGroup()
1133 || (isBelowSpeedBump() && !NotificationShelf.SHOW_AMBIENT_ICONS)
1134 || mIconsVisible;
Adrian Rooseb434ff2017-01-11 11:18:48 -08001135 for (NotificationContentView l : mLayouts) {
1136 l.setIconsVisible(visible);
1137 }
Selim Cinekdb167372016-11-17 15:41:17 -08001138 if (mChildrenContainer != null) {
1139 mChildrenContainer.setIconsVisible(visible);
Selim Cinek0242fbb2016-10-19 13:38:32 -07001140 }
1141 }
1142
Selim Cinek875a3a12016-11-18 17:52:16 -08001143 /**
1144 * Get the relative top padding of a view relative to this view. This recursively walks up the
1145 * hierarchy and does the corresponding measuring.
1146 *
1147 * @param view the view to the the padding for. The requested view has to be a child of this
1148 * notification.
1149 * @return the toppadding
1150 */
1151 public int getRelativeTopPadding(View view) {
1152 int topPadding = 0;
1153 while (view.getParent() instanceof ViewGroup) {
1154 topPadding += view.getTop();
1155 view = (View) view.getParent();
1156 if (view instanceof ExpandableNotificationRow) {
1157 return topPadding;
1158 }
1159 }
1160 return topPadding;
1161 }
1162
Selim Cineka1d97902016-12-14 16:31:40 -08001163 public float getContentTranslation() {
1164 return mPrivateLayout.getTranslationY();
1165 }
1166
Selim Cinek6743c0b2017-01-18 18:24:01 -08001167 public void setIsLowPriority(boolean isLowPriority) {
1168 mIsLowPriority = isLowPriority;
1169 mPrivateLayout.setIsLowPriority(isLowPriority);
Selim Cinek1a48bab2017-02-17 19:38:40 -08001170 mNotificationInflater.setIsLowPriority(mIsLowPriority);
Selim Cinek6743c0b2017-01-18 18:24:01 -08001171 if (mChildrenContainer != null) {
1172 mChildrenContainer.setIsLowPriority(isLowPriority);
1173 }
1174 }
1175
Selim Cinek55a3e732017-05-25 18:30:10 -07001176
1177 public void setLowPriorityStateUpdated(boolean lowPriorityStateUpdated) {
1178 mLowPriorityStateUpdated = lowPriorityStateUpdated;
1179 }
1180
1181 public boolean hasLowPriorityStateUpdated() {
1182 return mLowPriorityStateUpdated;
1183 }
1184
Selim Cinek414ad332017-02-24 19:06:12 -08001185 public boolean isLowPriority() {
1186 return mIsLowPriority;
1187 }
1188
Selim Cinek7d1009b2017-01-25 15:28:28 -08001189 public void setUseIncreasedCollapsedHeight(boolean use) {
1190 mUseIncreasedCollapsedHeight = use;
Selim Cinek1a48bab2017-02-17 19:38:40 -08001191 mNotificationInflater.setUsesIncreasedHeight(use);
Selim Cinek7d1009b2017-01-25 15:28:28 -08001192 }
1193
Selim Cinek87ed69b2017-02-09 15:59:43 -08001194 public void setUseIncreasedHeadsUpHeight(boolean use) {
1195 mUseIncreasedHeadsUpHeight = use;
Selim Cinek1a48bab2017-02-17 19:38:40 -08001196 mNotificationInflater.setUsesIncreasedHeadsUpHeight(use);
1197 }
1198
1199 public void setRemoteViewClickHandler(RemoteViews.OnClickHandler remoteViewClickHandler) {
1200 mNotificationInflater.setRemoteViewClickHandler(remoteViewClickHandler);
Selim Cinek87ed69b2017-02-09 15:59:43 -08001201 }
1202
Selim Cinek5ba22542017-04-20 15:16:10 -07001203 public void setInflationCallback(InflationCallback callback) {
1204 mNotificationInflater.setInflationCallback(callback);
Selim Cinekc478f902017-02-22 20:55:44 -08001205 }
1206
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07001207 public void setNeedsRedaction(boolean needsRedaction) {
1208 mNotificationInflater.setRedactAmbient(needsRedaction);
1209 }
1210
Selim Cinek5ba22542017-04-20 15:16:10 -07001211 @VisibleForTesting
1212 public NotificationInflater getNotificationInflater() {
1213 return mNotificationInflater;
1214 }
1215
Adrian Roos6f6e1592017-05-02 16:22:53 -07001216 public int getNotificationColorAmbient() {
1217 return mNotificationColorAmbient;
1218 }
1219
Chris Wren78403d72014-07-28 10:23:24 +01001220 public interface ExpansionLogger {
Anthony Chen6bf88a02017-04-10 14:41:44 -07001221 void logNotificationExpansion(String key, boolean userAction, boolean expanded);
Chris Wren78403d72014-07-28 10:23:24 +01001222 }
Selim Cinek1685e632014-04-08 02:27:49 +02001223
Chris Wren51c75102013-07-16 20:49:17 -04001224 public ExpandableNotificationRow(Context context, AttributeSet attrs) {
1225 super(context, attrs);
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -07001226 mFalsingManager = FalsingManager.getInstance(context);
Selim Cinek1a48bab2017-02-17 19:38:40 -08001227 mNotificationInflater = new NotificationInflater(this);
Mady Mellor95d743c2017-01-10 12:05:27 -08001228 mMenuRow = new NotificationMenuRow(mContext);
Selim Cinek01af3342016-02-09 19:25:31 -08001229 initDimens();
1230 }
1231
1232 private void initDimens() {
Selim Cinekf619ffc2016-02-17 14:53:05 -08001233 mNotificationMinHeightLegacy = getFontScaledHeight(R.dimen.notification_min_height_legacy);
1234 mNotificationMinHeight = getFontScaledHeight(R.dimen.notification_min_height);
Selim Cinek7d1009b2017-01-25 15:28:28 -08001235 mNotificationMinHeightLarge = getFontScaledHeight(
Selim Cinek87ed69b2017-02-09 15:59:43 -08001236 R.dimen.notification_min_height_increased);
Selim Cinekf619ffc2016-02-17 14:53:05 -08001237 mNotificationMaxHeight = getFontScaledHeight(R.dimen.notification_max_height);
Adrian Roos0aac04f2016-12-08 15:59:29 -08001238 mNotificationAmbientHeight = getFontScaledHeight(R.dimen.notification_ambient_height);
Selim Cinekf619ffc2016-02-17 14:53:05 -08001239 mMaxHeadsUpHeightLegacy = getFontScaledHeight(
Selim Cinek77019c72015-12-09 10:18:02 -08001240 R.dimen.notification_max_heads_up_height_legacy);
Selim Cinekf619ffc2016-02-17 14:53:05 -08001241 mMaxHeadsUpHeight = getFontScaledHeight(R.dimen.notification_max_heads_up_height);
Selim Cinek87ed69b2017-02-09 15:59:43 -08001242 mMaxHeadsUpHeightIncreased = getFontScaledHeight(
1243 R.dimen.notification_max_heads_up_height_increased);
Anthony Chen6bf88a02017-04-10 14:41:44 -07001244
Anthony Chen7acbb772017-04-07 16:45:25 -07001245 Resources res = getResources();
1246 mIncreasedPaddingBetweenElements = res.getDimensionPixelSize(
1247 R.dimen.notification_divider_height_increased);
1248 mIconTransformContentShiftNoIcon = res.getDimensionPixelSize(
Selim Cinek0242fbb2016-10-19 13:38:32 -07001249 R.dimen.notification_icon_transform_content_shift);
Anthony Chen6bf88a02017-04-10 14:41:44 -07001250 mEnableNonGroupedNotificationExpand =
1251 res.getBoolean(R.bool.config_enableNonGroupedNotificationExpand);
1252 mShowGroupBackgroundWhenExpanded =
1253 res.getBoolean(R.bool.config_showGroupNotificationBgWhenExpanded);
Selim Cinekf619ffc2016-02-17 14:53:05 -08001254 }
1255
1256 /**
1257 * @param dimenId the dimen to look up
1258 * @return the font scaled dimen as if it were in sp but doesn't shrink sizes below dp
1259 */
1260 private int getFontScaledHeight(int dimenId) {
1261 int dimensionPixelSize = getResources().getDimensionPixelSize(dimenId);
1262 float factor = Math.max(1.0f, getResources().getDisplayMetrics().scaledDensity /
1263 getResources().getDisplayMetrics().density);
1264 return (int) (dimensionPixelSize * factor);
Chris Wren51c75102013-07-16 20:49:17 -04001265 }
1266
Christoph Studera7fe6312014-06-27 19:32:44 +02001267 /**
1268 * Resets this view so it can be re-used for an updated notification.
1269 */
1270 public void reset() {
Jorim Jaggiae441282014-08-01 02:45:18 +02001271 mShowingPublicInitialized = false;
Selim Cinek31094df2014-08-14 19:28:15 +02001272 onHeightReset();
Selim Cinek6e28a672014-09-05 14:43:28 +02001273 requestLayout();
Christoph Studera7fe6312014-06-27 19:32:44 +02001274 }
1275
Jorim Jaggi251957d2014-04-09 04:24:09 +02001276 @Override
1277 protected void onFinishInflate() {
1278 super.onFinishInflate();
Jorim Jaggibe565df2014-04-28 17:51:23 +02001279 mPublicLayout = (NotificationContentView) findViewById(R.id.expandedPublic);
1280 mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded);
Adrian Rooseb434ff2017-01-11 11:18:48 -08001281 mLayouts = new NotificationContentView[] {mPrivateLayout, mPublicLayout};
1282
1283 for (NotificationContentView l : mLayouts) {
1284 l.setExpandClickListener(mExpandClickListener);
1285 l.setContainingNotification(this);
1286 }
Selim Cinekab29aeb2015-02-20 18:18:32 +01001287 mGutsStub = (ViewStub) findViewById(R.id.notification_guts_stub);
1288 mGutsStub.setOnInflateListener(new ViewStub.OnInflateListener() {
Jorim Jaggib1cd3c12014-09-08 19:55:17 +02001289 @Override
1290 public void onInflate(ViewStub stub, View inflated) {
1291 mGuts = (NotificationGuts) inflated;
1292 mGuts.setClipTopAmount(getClipTopAmount());
1293 mGuts.setActualHeight(getActualHeight());
Selim Cinekab29aeb2015-02-20 18:18:32 +01001294 mGutsStub = null;
Jorim Jaggib1cd3c12014-09-08 19:55:17 +02001295 }
1296 });
Selim Cinekb5605e52015-02-20 18:21:41 +01001297 mChildrenContainerStub = (ViewStub) findViewById(R.id.child_container_stub);
1298 mChildrenContainerStub.setOnInflateListener(new ViewStub.OnInflateListener() {
1299
1300 @Override
1301 public void onInflate(ViewStub stub, View inflated) {
1302 mChildrenContainer = (NotificationChildrenContainer) inflated;
Selim Cinek6743c0b2017-01-18 18:24:01 -08001303 mChildrenContainer.setIsLowPriority(mIsLowPriority);
Selim Cinek414ad332017-02-24 19:06:12 -08001304 mChildrenContainer.setContainingNotification(ExpandableNotificationRow.this);
Selim Cinekc897bd32016-03-18 17:32:31 -07001305 mChildrenContainer.onNotificationUpdated();
Anthony Chen7acbb772017-04-07 16:45:25 -07001306
1307 if (mShouldTranslateContents) {
1308 mTranslateableViews.add(mChildrenContainer);
1309 }
Selim Cinekb5605e52015-02-20 18:21:41 +01001310 }
1311 });
Mady Mellor4b80b102016-01-22 08:03:58 -08001312
Anthony Chen7acbb772017-04-07 16:45:25 -07001313 if (mShouldTranslateContents) {
1314 // Add the views that we translate to reveal the menu
1315 mTranslateableViews = new ArrayList<>();
1316 for (int i = 0; i < getChildCount(); i++) {
1317 mTranslateableViews.add(getChildAt(i));
1318 }
1319 // Remove views that don't translate
1320 mTranslateableViews.remove(mChildrenContainerStub);
1321 mTranslateableViews.remove(mGutsStub);
Mady Mellor4b80b102016-01-22 08:03:58 -08001322 }
Mady Mellor4b80b102016-01-22 08:03:58 -08001323 }
1324
Mady Mellor4b80b102016-01-22 08:03:58 -08001325 public void resetTranslation() {
Mady Mellor32c638a2016-09-14 08:58:25 -07001326 if (mTranslateAnim != null) {
1327 mTranslateAnim.cancel();
1328 }
Anthony Chen7acbb772017-04-07 16:45:25 -07001329
1330 if (!mShouldTranslateContents) {
1331 setTranslationX(0);
1332 } else if (mTranslateableViews != null) {
Mady Mellor4b80b102016-01-22 08:03:58 -08001333 for (int i = 0; i < mTranslateableViews.size(); i++) {
1334 mTranslateableViews.get(i).setTranslationX(0);
1335 }
Anthony Chen7acbb772017-04-07 16:45:25 -07001336 invalidateOutline();
Mady Mellor4b80b102016-01-22 08:03:58 -08001337 }
Anthony Chen7acbb772017-04-07 16:45:25 -07001338
Mady Mellor95d743c2017-01-10 12:05:27 -08001339 mMenuRow.resetMenu();
Mady Mellor4b80b102016-01-22 08:03:58 -08001340 }
1341
1342 public void animateTranslateNotification(final float leftTarget) {
1343 if (mTranslateAnim != null) {
1344 mTranslateAnim.cancel();
1345 }
Mady Mellorb0a82462016-04-30 17:31:02 -07001346 mTranslateAnim = getTranslateViewAnimator(leftTarget, null /* updateListener */);
Mady Mellor34958fa2016-02-23 09:52:17 -08001347 if (mTranslateAnim != null) {
1348 mTranslateAnim.start();
1349 }
1350 }
1351
1352 @Override
1353 public void setTranslation(float translationX) {
1354 if (areGutsExposed()) {
1355 // Don't translate if guts are showing.
1356 return;
1357 }
Anthony Chen7acbb772017-04-07 16:45:25 -07001358 if (!mShouldTranslateContents) {
1359 setTranslationX(translationX);
1360 } else if (mTranslateableViews != null) {
1361 // Translate the group of views
1362 for (int i = 0; i < mTranslateableViews.size(); i++) {
1363 if (mTranslateableViews.get(i) != null) {
1364 mTranslateableViews.get(i).setTranslationX(translationX);
1365 }
Mady Mellor34958fa2016-02-23 09:52:17 -08001366 }
Anthony Chen7acbb772017-04-07 16:45:25 -07001367 invalidateOutline();
Mady Mellor34958fa2016-02-23 09:52:17 -08001368 }
Mady Mellor95d743c2017-01-10 12:05:27 -08001369 if (mMenuRow.getMenuView() != null) {
1370 mMenuRow.onTranslationUpdate(translationX);
Mady Mellor34958fa2016-02-23 09:52:17 -08001371 }
1372 }
1373
1374 @Override
1375 public float getTranslation() {
Anthony Chene04afcb2017-04-21 13:51:32 -07001376 if (!mShouldTranslateContents) {
Anthony Chen7acbb772017-04-07 16:45:25 -07001377 return getTranslationX();
1378 }
1379
Mady Mellor34958fa2016-02-23 09:52:17 -08001380 if (mTranslateableViews != null && mTranslateableViews.size() > 0) {
1381 // All of the views in the list should have same translation, just use first one.
1382 return mTranslateableViews.get(0).getTranslationX();
1383 }
Anthony Chen7acbb772017-04-07 16:45:25 -07001384
Mady Mellor34958fa2016-02-23 09:52:17 -08001385 return 0;
1386 }
1387
1388 public Animator getTranslateViewAnimator(final float leftTarget,
1389 AnimatorUpdateListener listener) {
Mady Mellor723f1f92016-03-13 15:54:06 -07001390 if (mTranslateAnim != null) {
1391 mTranslateAnim.cancel();
1392 }
Mady Mellor34958fa2016-02-23 09:52:17 -08001393 if (areGutsExposed()) {
1394 // No translation if guts are exposed.
1395 return null;
1396 }
Mady Mellorb0a82462016-04-30 17:31:02 -07001397 final ObjectAnimator translateAnim = ObjectAnimator.ofFloat(this, TRANSLATE_CONTENT,
1398 leftTarget);
1399 if (listener != null) {
1400 translateAnim.addUpdateListener(listener);
Mady Mellor4b80b102016-01-22 08:03:58 -08001401 }
Mady Mellorb0a82462016-04-30 17:31:02 -07001402 translateAnim.addListener(new AnimatorListenerAdapter() {
1403 boolean cancelled = false;
1404
1405 @Override
1406 public void onAnimationCancel(Animator anim) {
1407 cancelled = true;
1408 }
1409
1410 @Override
1411 public void onAnimationEnd(Animator anim) {
Mady Mellor95d743c2017-01-10 12:05:27 -08001412 if (!cancelled && leftTarget == 0) {
1413 mMenuRow.resetMenu();
Mady Mellorb0a82462016-04-30 17:31:02 -07001414 mTranslateAnim = null;
1415 }
1416 }
1417 });
1418 mTranslateAnim = translateAnim;
1419 return translateAnim;
Mady Mellor4b80b102016-01-22 08:03:58 -08001420 }
1421
Selim Cinekab29aeb2015-02-20 18:18:32 +01001422 public void inflateGuts() {
1423 if (mGuts == null) {
1424 mGutsStub.inflate();
1425 }
1426 }
1427
Selim Cinekda42d652015-12-04 15:51:16 -08001428 private void updateChildrenVisibility() {
Selim Cinekd84a5932015-12-15 11:45:36 -08001429 mPrivateLayout.setVisibility(!mShowingPublic && !mIsSummaryWithChildren ? VISIBLE
1430 : INVISIBLE);
Selim Cinekef5127e2015-12-21 16:55:58 -08001431 if (mChildrenContainer != null) {
1432 mChildrenContainer.setVisibility(!mShowingPublic && mIsSummaryWithChildren ? VISIBLE
1433 : INVISIBLE);
Selim Cinekef5127e2015-12-21 16:55:58 -08001434 }
Selim Cinekda42d652015-12-04 15:51:16 -08001435 // The limits might have changed if the view suddenly became a group or vice versa
1436 updateLimits();
Selim Cinekb5605e52015-02-20 18:21:41 +01001437 }
1438
Jorim Jaggife40f7d2014-04-28 15:20:04 +02001439 @Override
Alan Viverettea54956a2015-01-07 16:05:02 -08001440 public boolean onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event) {
1441 if (super.onRequestSendAccessibilityEventInternal(child, event)) {
Jorim Jaggife40f7d2014-04-28 15:20:04 +02001442 // Add a record for the entire layout since its content is somehow small.
1443 // The event comes from a leaf view that is interacted with.
1444 AccessibilityEvent record = AccessibilityEvent.obtain();
1445 onInitializeAccessibilityEvent(record);
1446 dispatchPopulateAccessibilityEvent(record);
1447 event.appendRecord(record);
1448 return true;
1449 }
1450 return false;
Jorim Jaggic5dc0d02014-04-15 15:42:55 +02001451 }
Chris Wren51c75102013-07-16 20:49:17 -04001452
John Spurlocke15452b2014-08-21 09:44:39 -04001453 @Override
Jorim Jaggi4e857f42014-11-17 19:14:04 +01001454 public void setDark(boolean dark, boolean fade, long delay) {
1455 super.setDark(dark, fade, delay);
Adrian Roos28f90c72017-05-08 17:24:26 -07001456 if (!mIsHeadsUp) {
1457 // Only fade the showing view of the pulsing notification.
1458 fade = false;
1459 }
John Spurlocke15452b2014-08-21 09:44:39 -04001460 final NotificationContentView showing = getShowingLayout();
1461 if (showing != null) {
Jorim Jaggi4e857f42014-11-17 19:14:04 +01001462 showing.setDark(dark, fade, delay);
John Spurlocke15452b2014-08-21 09:44:39 -04001463 }
Selim Cinek9c7712d2015-12-08 19:19:48 -08001464 if (mIsSummaryWithChildren) {
Selim Cinekc897bd32016-03-18 17:32:31 -07001465 mChildrenContainer.setDark(dark, fade, delay);
Selim Cinek9c7712d2015-12-08 19:19:48 -08001466 }
Selim Cineka7c69632017-06-07 15:51:29 -07001467 updateShelfIconColor();
John Spurlocke15452b2014-08-21 09:44:39 -04001468 }
1469
Chris Wren51c75102013-07-16 20:49:17 -04001470 public boolean isExpandable() {
Selim Cinek388df6d2015-10-22 13:25:11 -07001471 if (mIsSummaryWithChildren && !mShowingPublic) {
1472 return !mChildrenExpanded;
1473 }
Anthony Chen6bf88a02017-04-10 14:41:44 -07001474 return mEnableNonGroupedNotificationExpand && mExpandable;
Chris Wren51c75102013-07-16 20:49:17 -04001475 }
1476
1477 public void setExpandable(boolean expandable) {
1478 mExpandable = expandable;
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001479 mPrivateLayout.updateExpandButtons(isExpandable());
Chris Wren51c75102013-07-16 20:49:17 -04001480 }
1481
Selim Cinek4ffd6362015-12-29 15:12:23 +01001482 @Override
1483 public void setClipToActualHeight(boolean clipToActualHeight) {
Selim Cinek084c16b2016-01-22 17:48:22 -08001484 super.setClipToActualHeight(clipToActualHeight || isUserLocked());
1485 getShowingLayout().setClipToActualHeight(clipToActualHeight || isUserLocked());
Selim Cinek4ffd6362015-12-29 15:12:23 +01001486 }
1487
Selim Cinek1685e632014-04-08 02:27:49 +02001488 /**
1489 * @return whether the user has changed the expansion state
1490 */
1491 public boolean hasUserChangedExpansion() {
1492 return mHasUserChangedExpansion;
1493 }
1494
Chris Wren51c75102013-07-16 20:49:17 -04001495 public boolean isUserExpanded() {
1496 return mUserExpanded;
1497 }
1498
Selim Cinek1685e632014-04-08 02:27:49 +02001499 /**
1500 * Set this notification to be expanded by the user
1501 *
1502 * @param userExpanded whether the user wants this notification to be expanded
1503 */
Chris Wren51c75102013-07-16 20:49:17 -04001504 public void setUserExpanded(boolean userExpanded) {
Selim Cinek388df6d2015-10-22 13:25:11 -07001505 setUserExpanded(userExpanded, false /* allowChildExpansion */);
1506 }
1507
1508 /**
1509 * Set this notification to be expanded by the user
1510 *
1511 * @param userExpanded whether the user wants this notification to be expanded
1512 * @param allowChildExpansion whether a call to this method allows expanding children
1513 */
1514 public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -07001515 mFalsingManager.setNotificationExpanded();
Selim Cinek414ad332017-02-24 19:06:12 -08001516 if (mIsSummaryWithChildren && !mShowingPublic && allowChildExpansion
1517 && !mChildrenContainer.showingAsLowPriority()) {
Chris Wren698b1702016-05-23 11:16:32 -04001518 final boolean wasExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
Selim Cinek388df6d2015-10-22 13:25:11 -07001519 mGroupManager.setGroupExpanded(mStatusBarNotification, userExpanded);
Selim Cinek414ad332017-02-24 19:06:12 -08001520 onExpansionChanged(true /* userAction */, wasExpanded);
Selim Cinek388df6d2015-10-22 13:25:11 -07001521 return;
1522 }
Christoph Studera7fe6312014-06-27 19:32:44 +02001523 if (userExpanded && !mExpandable) return;
Chris Wren78403d72014-07-28 10:23:24 +01001524 final boolean wasExpanded = isExpanded();
Selim Cinek1685e632014-04-08 02:27:49 +02001525 mHasUserChangedExpansion = true;
Chris Wren51c75102013-07-16 20:49:17 -04001526 mUserExpanded = userExpanded;
Selim Cinek414ad332017-02-24 19:06:12 -08001527 onExpansionChanged(true /* userAction */, wasExpanded);
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07001528 if (!wasExpanded && isExpanded()
1529 && getActualHeight() != getIntrinsicHeight()) {
1530 notifyHeightChanged(true /* needsAnimation */);
1531 }
Chris Wren51c75102013-07-16 20:49:17 -04001532 }
1533
Selim Cinekccd14fb2014-08-12 18:53:24 +02001534 public void resetUserExpansion() {
Selim Cinek414ad332017-02-24 19:06:12 -08001535 boolean changed = mUserExpanded;
Selim Cinekccd14fb2014-08-12 18:53:24 +02001536 mHasUserChangedExpansion = false;
1537 mUserExpanded = false;
Selim Cinek414ad332017-02-24 19:06:12 -08001538 if (changed && mIsSummaryWithChildren) {
1539 mChildrenContainer.onExpansionChanged();
1540 }
1541 updateShelfIconColor();
Selim Cinekccd14fb2014-08-12 18:53:24 +02001542 }
1543
Chris Wren51c75102013-07-16 20:49:17 -04001544 public boolean isUserLocked() {
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001545 return mUserLocked && !mForceUnlocked;
Chris Wren51c75102013-07-16 20:49:17 -04001546 }
1547
1548 public void setUserLocked(boolean userLocked) {
1549 mUserLocked = userLocked;
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001550 mPrivateLayout.setUserExpanding(userLocked);
Selim Cinek817abe72017-05-24 11:08:55 -07001551 // This is intentionally not guarded with mIsSummaryWithChildren since we might have had
1552 // children but not anymore.
1553 if (mChildrenContainer != null) {
Selim Cinek42357e02016-02-24 18:48:01 -08001554 mChildrenContainer.setUserLocked(userLocked);
Selim Cinek817abe72017-05-24 11:08:55 -07001555 if (mIsSummaryWithChildren && (userLocked || !isGroupExpanded())) {
Mady Mellorb0a82462016-04-30 17:31:02 -07001556 updateBackgroundForGroupState();
1557 }
Selim Cinek42357e02016-02-24 18:48:01 -08001558 }
Chris Wren51c75102013-07-16 20:49:17 -04001559 }
1560
Selim Cinek1685e632014-04-08 02:27:49 +02001561 /**
1562 * @return has the system set this notification to be expanded
1563 */
1564 public boolean isSystemExpanded() {
1565 return mIsSystemExpanded;
1566 }
1567
1568 /**
1569 * Set this notification to be expanded by the system.
1570 *
1571 * @param expand whether the system wants this notification to be expanded.
1572 */
1573 public void setSystemExpanded(boolean expand) {
Selim Cinek31094df2014-08-14 19:28:15 +02001574 if (expand != mIsSystemExpanded) {
1575 final boolean wasExpanded = isExpanded();
1576 mIsSystemExpanded = expand;
Selim Cinekb5605e52015-02-20 18:21:41 +01001577 notifyHeightChanged(false /* needsAnimation */);
Selim Cinek414ad332017-02-24 19:06:12 -08001578 onExpansionChanged(false /* userAction */, wasExpanded);
Selim Cineked6913b2016-06-01 12:01:17 -07001579 if (mIsSummaryWithChildren) {
Selim Cinekc897bd32016-03-18 17:32:31 -07001580 mChildrenContainer.updateGroupOverflow();
1581 }
Selim Cinek31094df2014-08-14 19:28:15 +02001582 }
Jorim Jaggidce3c4c2014-04-29 23:12:24 +02001583 }
1584
1585 /**
Selim Cinek83bc7832015-10-22 13:26:54 -07001586 * @param onKeyguard whether to prevent notification expansion
Jorim Jaggidce3c4c2014-04-29 23:12:24 +02001587 */
Selim Cinek83bc7832015-10-22 13:26:54 -07001588 public void setOnKeyguard(boolean onKeyguard) {
1589 if (onKeyguard != mOnKeyguard) {
Selim Cinek5cf1d052017-06-01 17:36:46 -07001590 boolean wasAboveShelf = isAboveShelf();
Selim Cinek31094df2014-08-14 19:28:15 +02001591 final boolean wasExpanded = isExpanded();
Selim Cinek83bc7832015-10-22 13:26:54 -07001592 mOnKeyguard = onKeyguard;
Selim Cinek414ad332017-02-24 19:06:12 -08001593 onExpansionChanged(false /* userAction */, wasExpanded);
Selim Cinek31094df2014-08-14 19:28:15 +02001594 if (wasExpanded != isExpanded()) {
Selim Cinekc897bd32016-03-18 17:32:31 -07001595 if (mIsSummaryWithChildren) {
1596 mChildrenContainer.updateGroupOverflow();
1597 }
Mady Mellor4b80b102016-01-22 08:03:58 -08001598 notifyHeightChanged(false /* needsAnimation */);
Selim Cinek31094df2014-08-14 19:28:15 +02001599 }
Selim Cinek5cf1d052017-06-01 17:36:46 -07001600 if (isAboveShelf() != wasAboveShelf) {
1601 mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
1602 }
Selim Cinek31094df2014-08-14 19:28:15 +02001603 }
Selim Cinek1685e632014-04-08 02:27:49 +02001604 }
1605
1606 /**
Selim Cinek9e624e72016-07-20 13:46:49 -07001607 * @return Can the underlying notification be cleared? This can be different from whether the
1608 * notification can be dismissed in case notifications are sensitive on the lockscreen.
1609 * @see #canViewBeDismissed()
Dan Sandler0d3e62f2014-07-14 17:13:50 -04001610 */
1611 public boolean isClearable() {
Selim Cinek506deb62016-07-20 15:43:59 -07001612 if (mStatusBarNotification == null || !mStatusBarNotification.isClearable()) {
1613 return false;
1614 }
1615 if (mIsSummaryWithChildren) {
1616 List<ExpandableNotificationRow> notificationChildren =
1617 mChildrenContainer.getNotificationChildren();
1618 for (int i = 0; i < notificationChildren.size(); i++) {
1619 ExpandableNotificationRow child = notificationChildren.get(i);
1620 if (!child.isClearable()) {
1621 return false;
1622 }
1623 }
1624 }
1625 return true;
Dan Sandler0d3e62f2014-07-14 17:13:50 -04001626 }
1627
Jorim Jaggi9cbadd32014-05-01 20:18:31 +02001628 @Override
1629 public int getIntrinsicHeight() {
Jorim Jaggibe565df2014-04-28 17:51:23 +02001630 if (isUserLocked()) {
1631 return getActualHeight();
1632 }
Geoffrey Pitsch4dd50062016-12-06 16:41:22 -05001633 if (mGuts != null && mGuts.isExposed()) {
Mady Mellore09fb702017-03-30 13:23:29 -07001634 return mGuts.getIntrinsicHeight();
Selim Cinekd84a5932015-12-15 11:45:36 -08001635 } else if ((isChildInGroup() && !isGroupExpanded())) {
1636 return mPrivateLayout.getMinHeight();
1637 } else if (mSensitive && mHideSensitiveForIntrinsicHeight) {
1638 return getMinHeight();
Adrian Roos6f6e1592017-05-02 16:22:53 -07001639 } else if (mIsSummaryWithChildren && (!mOnKeyguard || mShowAmbient)) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001640 return mChildrenContainer.getIntrinsicHeight();
Adrian Roos6f6e1592017-05-02 16:22:53 -07001641 } else if (isHeadsUpAllowed() && (mIsHeadsUp || mHeadsupDisappearRunning)) {
Selim Cinek73cf02a2016-06-17 13:08:00 -07001642 if (isPinned() || mHeadsupDisappearRunning) {
Selim Cinek31aada42015-12-18 17:51:15 -08001643 return getPinnedHeadsUpHeight(true /* atLeastMinHeight */);
1644 } else if (isExpanded()) {
Selim Cinekd84a5932015-12-15 11:45:36 -08001645 return Math.max(getMaxExpandHeight(), mHeadsUpHeight);
Selim Cinek8d490d42015-04-10 00:05:50 -07001646 } else {
Selim Cinek567e8452016-03-24 10:54:56 -07001647 return Math.max(getCollapsedHeight(), mHeadsUpHeight);
Selim Cinek8d490d42015-04-10 00:05:50 -07001648 }
Selim Cinek31aada42015-12-18 17:51:15 -08001649 } else if (isExpanded()) {
Selim Cinek83bc7832015-10-22 13:26:54 -07001650 return getMaxExpandHeight();
Selim Cinekd84a5932015-12-15 11:45:36 -08001651 } else {
Selim Cinek567e8452016-03-24 10:54:56 -07001652 return getCollapsedHeight();
Selim Cinek1685e632014-04-08 02:27:49 +02001653 }
Selim Cinekb5605e52015-02-20 18:21:41 +01001654 }
Selim Cinek1685e632014-04-08 02:27:49 +02001655
Adrian Roos6f6e1592017-05-02 16:22:53 -07001656 private boolean isHeadsUpAllowed() {
1657 return !mOnKeyguard && !mShowAmbient;
1658 }
1659
Mady Mellor43c2cd12016-12-12 21:05:13 -08001660 @Override
Mady Mellorb0a82462016-04-30 17:31:02 -07001661 public boolean isGroupExpanded() {
Selim Cinekeef84282015-10-30 16:28:00 -07001662 return mGroupManager.isGroupExpanded(mStatusBarNotification);
Selim Cinekb5605e52015-02-20 18:21:41 +01001663 }
1664
Selim Cinek263398f2015-10-21 17:40:23 -07001665 private void onChildrenCountChanged() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05001666 mIsSummaryWithChildren = StatusBar.ENABLE_CHILD_NOTIFICATIONS
Mady Mellorb0a82462016-04-30 17:31:02 -07001667 && mChildrenContainer != null && mChildrenContainer.getNotificationChildCount() > 0;
1668 if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() == null) {
Selim Cinek414ad332017-02-24 19:06:12 -08001669 mChildrenContainer.recreateNotificationHeader(mExpandClickListener
1670 );
Selim Cinek263398f2015-10-21 17:40:23 -07001671 }
Mady Mellor6baed9e2016-05-25 16:05:09 -07001672 getShowingLayout().updateBackgroundColor(false /* animate */);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001673 mPrivateLayout.updateExpandButtons(isExpandable());
Selim Cinekea4bef72015-12-02 15:51:10 -08001674 updateChildrenHeaderAppearance();
Selim Cinekda42d652015-12-04 15:51:16 -08001675 updateChildrenVisibility();
Selim Cinek263398f2015-10-21 17:40:23 -07001676 }
1677
Mady Mellorb0a82462016-04-30 17:31:02 -07001678 public void updateChildrenHeaderAppearance() {
Selim Cineked6913b2016-06-01 12:01:17 -07001679 if (mIsSummaryWithChildren) {
Mady Mellorb0a82462016-04-30 17:31:02 -07001680 mChildrenContainer.updateChildrenHeaderAppearance();
1681 }
1682 }
1683
Selim Cinek1685e632014-04-08 02:27:49 +02001684 /**
1685 * Check whether the view state is currently expanded. This is given by the system in {@link
1686 * #setSystemExpanded(boolean)} and can be overridden by user expansion or
1687 * collapsing in {@link #setUserExpanded(boolean)}. Note that the visual appearance of this
1688 * view can differ from this state, if layout params are modified from outside.
1689 *
1690 * @return whether the view state is currently expanded.
1691 */
Selim Cinek83bc7832015-10-22 13:26:54 -07001692 public boolean isExpanded() {
Selim Cineke81b82b2016-03-04 11:22:28 -08001693 return isExpanded(false /* allowOnKeyguard */);
1694 }
1695
1696 public boolean isExpanded(boolean allowOnKeyguard) {
1697 return (!mOnKeyguard || allowOnKeyguard)
Selim Cinekb5605e52015-02-20 18:21:41 +01001698 && (!hasUserChangedExpansion() && (isSystemExpanded() || isSystemChildExpanded())
1699 || isUserExpanded());
1700 }
1701
1702 private boolean isSystemChildExpanded() {
1703 return mIsSystemChildExpanded;
1704 }
1705
1706 public void setSystemChildExpanded(boolean expanded) {
1707 mIsSystemChildExpanded = expanded;
Selim Cinek1685e632014-04-08 02:27:49 +02001708 }
1709
Mady Mellor035badd2017-04-04 18:45:30 -07001710 public void setLayoutListener(LayoutListener listener) {
1711 mLayoutListener = listener;
1712 }
1713
1714 public void removeListener() {
1715 mLayoutListener = null;
1716 }
1717
Selim Cinek1685e632014-04-08 02:27:49 +02001718 @Override
1719 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
1720 super.onLayout(changed, left, top, right, bottom);
Selim Cinek8d490d42015-04-10 00:05:50 -07001721 updateMaxHeights();
Mady Mellor95d743c2017-01-10 12:05:27 -08001722 if (mMenuRow.getMenuView() != null) {
1723 mMenuRow.onHeightUpdate();
Mady Mellora6edc872016-04-26 11:01:03 -07001724 }
Selim Cinek875a3a12016-11-18 17:52:16 -08001725 updateContentShiftHeight();
Mady Mellor035badd2017-04-04 18:45:30 -07001726 if (mLayoutListener != null) {
1727 mLayoutListener.onLayout();
1728 }
Selim Cinek875a3a12016-11-18 17:52:16 -08001729 }
1730
1731 /**
1732 * Updates the content shift height such that the header is completely hidden when coming from
1733 * the top.
1734 */
1735 private void updateContentShiftHeight() {
Selim Cinek414ad332017-02-24 19:06:12 -08001736 NotificationHeaderView notificationHeader = getVisibleNotificationHeader();
Selim Cinek875a3a12016-11-18 17:52:16 -08001737 if (notificationHeader != null) {
1738 CachingIconView icon = notificationHeader.getIcon();
1739 mIconTransformContentShift = getRelativeTopPadding(icon) + icon.getHeight();
1740 } else {
1741 mIconTransformContentShift = mIconTransformContentShiftNoIcon;
1742 }
Selim Cinek1685e632014-04-08 02:27:49 +02001743 }
1744
Selim Cinek8d490d42015-04-10 00:05:50 -07001745 private void updateMaxHeights() {
Selim Cinekd2319fb2014-09-01 19:41:54 +02001746 int intrinsicBefore = getIntrinsicHeight();
Selim Cinek8d490d42015-04-10 00:05:50 -07001747 View expandedChild = mPrivateLayout.getExpandedChild();
1748 if (expandedChild == null) {
1749 expandedChild = mPrivateLayout.getContractedChild();
1750 }
1751 mMaxExpandHeight = expandedChild.getHeight();
1752 View headsUpChild = mPrivateLayout.getHeadsUpChild();
Selim Cinek1f3f5442015-04-10 17:54:46 -07001753 if (headsUpChild == null) {
1754 headsUpChild = mPrivateLayout.getContractedChild();
Selim Cinek8d490d42015-04-10 00:05:50 -07001755 }
Selim Cinek1f3f5442015-04-10 17:54:46 -07001756 mHeadsUpHeight = headsUpChild.getHeight();
Selim Cinekd2319fb2014-09-01 19:41:54 +02001757 if (intrinsicBefore != getIntrinsicHeight()) {
Selim Cinekbb42b7d2016-08-10 13:02:38 -07001758 notifyHeightChanged(true /* needsAnimation */);
Selim Cinekd2319fb2014-09-01 19:41:54 +02001759 }
1760 }
1761
Selim Cinekfa0a2d32016-01-14 13:02:21 -08001762 @Override
1763 public void notifyHeightChanged(boolean needsAnimation) {
1764 super.notifyHeightChanged(needsAnimation);
1765 getShowingLayout().requestSelectLayout(needsAnimation || isUserLocked());
1766 }
1767
Selim Cinek3c76d502016-02-19 15:16:33 -08001768 public void setSensitive(boolean sensitive, boolean hideSensitive) {
Jorim Jaggiae441282014-08-01 02:45:18 +02001769 mSensitive = sensitive;
Selim Cinek3c76d502016-02-19 15:16:33 -08001770 mSensitiveHiddenInGeneral = hideSensitive;
Jorim Jaggiae441282014-08-01 02:45:18 +02001771 }
1772
Mady Mellor43c2cd12016-12-12 21:05:13 -08001773 @Override
Jorim Jaggiae441282014-08-01 02:45:18 +02001774 public void setHideSensitiveForIntrinsicHeight(boolean hideSensitive) {
Selim Cinek60122be2015-04-15 18:16:50 -07001775 mHideSensitiveForIntrinsicHeight = hideSensitive;
Selim Cineka52f6a12016-02-29 15:35:58 -08001776 if (mIsSummaryWithChildren) {
1777 List<ExpandableNotificationRow> notificationChildren =
1778 mChildrenContainer.getNotificationChildren();
1779 for (int i = 0; i < notificationChildren.size(); i++) {
1780 ExpandableNotificationRow child = notificationChildren.get(i);
1781 child.setHideSensitiveForIntrinsicHeight(hideSensitive);
1782 }
1783 }
Jorim Jaggiae441282014-08-01 02:45:18 +02001784 }
1785
Mady Mellor43c2cd12016-12-12 21:05:13 -08001786 @Override
Jorim Jaggiae441282014-08-01 02:45:18 +02001787 public void setHideSensitive(boolean hideSensitive, boolean animated, long delay,
1788 long duration) {
1789 boolean oldShowingPublic = mShowingPublic;
1790 mShowingPublic = mSensitive && hideSensitive;
1791 if (mShowingPublicInitialized && mShowingPublic == oldShowingPublic) {
1792 return;
1793 }
Dan Sandlera5e0f412014-01-23 15:11:54 -05001794
1795 // bail out if no public version
Selim Cinek1685e632014-04-08 02:27:49 +02001796 if (mPublicLayout.getChildCount() == 0) return;
Dan Sandlera5e0f412014-01-23 15:11:54 -05001797
Jorim Jaggiae441282014-08-01 02:45:18 +02001798 if (!animated) {
1799 mPublicLayout.animate().cancel();
1800 mPrivateLayout.animate().cancel();
Selim Cineka554c702016-06-17 18:02:12 -07001801 if (mChildrenContainer != null) {
1802 mChildrenContainer.animate().cancel();
1803 mChildrenContainer.setAlpha(1f);
1804 }
Jorim Jaggiae441282014-08-01 02:45:18 +02001805 mPublicLayout.setAlpha(1f);
1806 mPrivateLayout.setAlpha(1f);
1807 mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE);
Selim Cinekd84a5932015-12-15 11:45:36 -08001808 updateChildrenVisibility();
Jorim Jaggiae441282014-08-01 02:45:18 +02001809 } else {
1810 animateShowingPublic(delay, duration);
1811 }
Selim Cinekc3179332016-03-04 14:44:56 -08001812 NotificationContentView showingLayout = getShowingLayout();
1813 showingLayout.updateBackgroundColor(animated);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001814 mPrivateLayout.updateExpandButtons(isExpandable());
Lucas Dupinb6ed63b2017-05-30 16:17:42 -07001815 updateShelfIconColor();
Adrian Roose5726a22016-12-19 14:26:51 -08001816 showingLayout.setDark(isDark(), false /* animate */, 0 /* delay */);
Jorim Jaggiae441282014-08-01 02:45:18 +02001817 mShowingPublicInitialized = true;
1818 }
1819
1820 private void animateShowingPublic(long delay, long duration) {
Mady Mellorb0a82462016-04-30 17:31:02 -07001821 View[] privateViews = mIsSummaryWithChildren
1822 ? new View[] {mChildrenContainer}
Selim Cinekd84a5932015-12-15 11:45:36 -08001823 : new View[] {mPrivateLayout};
1824 View[] publicViews = new View[] {mPublicLayout};
1825 View[] hiddenChildren = mShowingPublic ? privateViews : publicViews;
1826 View[] shownChildren = mShowingPublic ? publicViews : privateViews;
1827 for (final View hiddenView : hiddenChildren) {
1828 hiddenView.setVisibility(View.VISIBLE);
1829 hiddenView.animate().cancel();
1830 hiddenView.animate()
1831 .alpha(0f)
1832 .setStartDelay(delay)
1833 .setDuration(duration)
1834 .withEndAction(new Runnable() {
1835 @Override
1836 public void run() {
1837 hiddenView.setVisibility(View.INVISIBLE);
1838 }
1839 });
1840 }
1841 for (View showView : shownChildren) {
1842 showView.setVisibility(View.VISIBLE);
1843 showView.setAlpha(0f);
1844 showView.animate().cancel();
1845 showView.animate()
1846 .alpha(1f)
1847 .setStartDelay(delay)
1848 .setDuration(duration);
1849 }
Dan Sandler0d3e62f2014-07-14 17:13:50 -04001850 }
1851
Mady Mellor43c2cd12016-12-12 21:05:13 -08001852 @Override
Selim Cinek3776fe02016-02-04 13:32:43 -08001853 public boolean mustStayOnScreen() {
1854 return mIsHeadsUp;
1855 }
1856
Selim Cinek9e624e72016-07-20 13:46:49 -07001857 /**
1858 * @return Whether this view is allowed to be dismissed. Only valid for visible notifications as
1859 * otherwise some state might not be updated. To request about the general clearability
1860 * see {@link #isClearable()}.
1861 */
1862 public boolean canViewBeDismissed() {
Selim Cineke9bad242016-06-15 11:46:37 -07001863 return isClearable() && (!mShowingPublic || !mSensitiveHiddenInGeneral);
Dan Sandlera5e0f412014-01-23 15:11:54 -05001864 }
Jorim Jaggi251957d2014-04-09 04:24:09 +02001865
Ricky Waicd35def2016-05-03 11:07:07 +01001866 public void makeActionsVisibile() {
1867 setUserExpanded(true, true);
1868 if (isChildInGroup()) {
1869 mGroupManager.setGroupExpanded(mStatusBarNotification, true);
1870 }
Selim Cinekbb42b7d2016-08-10 13:02:38 -07001871 notifyHeightChanged(false /* needsAnimation */);
Ricky Waicd35def2016-05-03 11:07:07 +01001872 }
1873
Selim Cinekb5605e52015-02-20 18:21:41 +01001874 public void setChildrenExpanded(boolean expanded, boolean animate) {
1875 mChildrenExpanded = expanded;
Selim Cinek83bc7832015-10-22 13:26:54 -07001876 if (mChildrenContainer != null) {
1877 mChildrenContainer.setChildrenExpanded(expanded);
1878 }
Mady Mellor1a5d8ea2016-06-09 10:42:42 -07001879 updateBackgroundForGroupState();
Selim Cinekddf1b392016-05-27 16:33:10 -07001880 updateClickAndFocus();
Selim Cinekb5605e52015-02-20 18:21:41 +01001881 }
1882
Selim Cinekb5605e52015-02-20 18:21:41 +01001883 public static void applyTint(View v, int color) {
1884 int alpha;
1885 if (color != 0) {
1886 alpha = COLORED_DIVIDER_ALPHA;
1887 } else {
1888 color = 0xff000000;
1889 alpha = DEFAULT_DIVIDER_ALPHA;
1890 }
1891 if (v.getBackground() instanceof ColorDrawable) {
1892 ColorDrawable background = (ColorDrawable) v.getBackground();
1893 background.mutate();
1894 background.setColor(color);
1895 background.setAlpha(alpha);
1896 }
1897 }
1898
Selim Cinek1685e632014-04-08 02:27:49 +02001899 public int getMaxExpandHeight() {
Selim Cinekb5605e52015-02-20 18:21:41 +01001900 return mMaxExpandHeight;
Chris Wren51c75102013-07-16 20:49:17 -04001901 }
Jorim Jaggi584a7aa2014-04-10 23:26:13 +02001902
Mady Mellor34958fa2016-02-23 09:52:17 -08001903 public boolean areGutsExposed() {
Geoffrey Pitsch4dd50062016-12-06 16:41:22 -05001904 return (mGuts != null && mGuts.isExposed());
Mady Mellor34958fa2016-02-23 09:52:17 -08001905 }
1906
Jorim Jaggibe565df2014-04-28 17:51:23 +02001907 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001908 public boolean isContentExpandable() {
Selim Cinekc0ac4af2017-03-03 15:13:48 -08001909 if (mIsSummaryWithChildren && !mShowingPublic) {
1910 return true;
1911 }
Selim Cinek2f0df8a2014-06-10 17:40:42 +02001912 NotificationContentView showingLayout = getShowingLayout();
1913 return showingLayout.isContentExpandable();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001914 }
1915
1916 @Override
Selim Cinek560e64d2015-06-09 19:58:11 -07001917 protected View getContentView() {
Selim Cinekaa3901a2016-08-05 11:04:37 -07001918 if (mIsSummaryWithChildren && !mShowingPublic) {
Selim Cineka5703182016-05-11 21:23:16 -04001919 return mChildrenContainer;
1920 }
Selim Cinek560e64d2015-06-09 19:58:11 -07001921 return getShowingLayout();
1922 }
1923
1924 @Override
Selim Cinekaa3901a2016-08-05 11:04:37 -07001925 protected void onAppearAnimationFinished(boolean wasAppearing) {
1926 super.onAppearAnimationFinished(wasAppearing);
1927 if (wasAppearing) {
1928 // During the animation the visible view might have changed, so let's make sure all
1929 // alphas are reset
1930 if (mChildrenContainer != null) {
1931 mChildrenContainer.setAlpha(1.0f);
1932 mChildrenContainer.setLayerType(LAYER_TYPE_NONE, null);
1933 }
Adrian Rooseb434ff2017-01-11 11:18:48 -08001934 for (NotificationContentView l : mLayouts) {
1935 l.setAlpha(1.0f);
1936 l.setLayerType(LAYER_TYPE_NONE, null);
1937 }
Selim Cinekaa3901a2016-08-05 11:04:37 -07001938 }
1939 }
1940
1941 @Override
Mady Mellorb0a82462016-04-30 17:31:02 -07001942 public int getExtraBottomPadding() {
1943 if (mIsSummaryWithChildren && isGroupExpanded()) {
1944 return mIncreasedPaddingBetweenElements;
1945 }
1946 return 0;
1947 }
1948
1949 @Override
Jorim Jaggid552d9d2014-05-07 19:41:13 +02001950 public void setActualHeight(int height, boolean notifyListeners) {
Selim Cinek4ca6c632017-02-23 18:03:37 -08001951 boolean changed = height != getActualHeight();
Selim Cinekb5605e52015-02-20 18:21:41 +01001952 super.setActualHeight(height, notifyListeners);
Selim Cinek4ca6c632017-02-23 18:03:37 -08001953 if (changed && isRemoved()) {
1954 // TODO: remove this once we found the gfx bug for this.
1955 // This is a hack since a removed view sometimes would just stay blank. it occured
1956 // when sending yourself a message and then clicking on it.
1957 ViewGroup parent = (ViewGroup) getParent();
1958 if (parent != null) {
1959 parent.invalidate();
1960 }
1961 }
Geoffrey Pitsch4dd50062016-12-06 16:41:22 -05001962 if (mGuts != null && mGuts.isExposed()) {
Mady Mellorb53bc272016-02-11 18:28:23 -08001963 mGuts.setActualHeight(height);
1964 return;
1965 }
Selim Cinekeef84282015-10-30 16:28:00 -07001966 int contentHeight = Math.max(getMinHeight(), height);
Adrian Rooseb434ff2017-01-11 11:18:48 -08001967 for (NotificationContentView l : mLayouts) {
1968 l.setContentHeight(contentHeight);
1969 }
Selim Cinek42357e02016-02-24 18:48:01 -08001970 if (mIsSummaryWithChildren) {
1971 mChildrenContainer.setActualHeight(height);
1972 }
Jorim Jaggib1cd3c12014-09-08 19:55:17 +02001973 if (mGuts != null) {
1974 mGuts.setActualHeight(height);
1975 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001976 }
1977
1978 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01001979 public int getMaxContentHeight() {
Selim Cinek83bc7832015-10-22 13:26:54 -07001980 if (mIsSummaryWithChildren && !mShowingPublic) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001981 return mChildrenContainer.getMaxContentHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -07001982 }
Selim Cinek2f0df8a2014-06-10 17:40:42 +02001983 NotificationContentView showingLayout = getShowingLayout();
1984 return showingLayout.getMaxHeight();
Jorim Jaggibe565df2014-04-28 17:51:23 +02001985 }
1986
1987 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001988 public int getMinHeight() {
Mady Mellore09fb702017-03-30 13:23:29 -07001989 if (mGuts != null && mGuts.isExposed()) {
1990 return mGuts.getIntrinsicHeight();
Adrian Roos6f6e1592017-05-02 16:22:53 -07001991 } else if (isHeadsUpAllowed() && mIsHeadsUp && mHeadsUpManager.isTrackingHeadsUp()) {
Selim Cinek31aada42015-12-18 17:51:15 -08001992 return getPinnedHeadsUpHeight(false /* atLeastMinHeight */);
1993 } else if (mIsSummaryWithChildren && !isGroupExpanded() && !mShowingPublic) {
Selim Cinekb55386d2015-12-16 17:26:49 -08001994 return mChildrenContainer.getMinHeight();
Adrian Roos6f6e1592017-05-02 16:22:53 -07001995 } else if (isHeadsUpAllowed() && mIsHeadsUp) {
Selim Cinek31aada42015-12-18 17:51:15 -08001996 return mHeadsUpHeight;
Selim Cinekb55386d2015-12-16 17:26:49 -08001997 }
Selim Cinek816c8e42015-11-19 12:00:45 -08001998 NotificationContentView showingLayout = getShowingLayout();
1999 return showingLayout.getMinHeight();
2000 }
2001
2002 @Override
Selim Cinek567e8452016-03-24 10:54:56 -07002003 public int getCollapsedHeight() {
Selim Cinek2c584612016-02-29 16:14:25 -08002004 if (mIsSummaryWithChildren && !mShowingPublic) {
Selim Cinek567e8452016-03-24 10:54:56 -07002005 return mChildrenContainer.getCollapsedHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -07002006 }
Selim Cinek816c8e42015-11-19 12:00:45 -08002007 return getMinHeight();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02002008 }
2009
2010 @Override
Jorim Jaggibe565df2014-04-28 17:51:23 +02002011 public void setClipTopAmount(int clipTopAmount) {
2012 super.setClipTopAmount(clipTopAmount);
Adrian Rooseb434ff2017-01-11 11:18:48 -08002013 for (NotificationContentView l : mLayouts) {
2014 l.setClipTopAmount(clipTopAmount);
2015 }
Jorim Jaggib1cd3c12014-09-08 19:55:17 +02002016 if (mGuts != null) {
2017 mGuts.setClipTopAmount(clipTopAmount);
2018 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02002019 }
2020
Selim Cineka686b2c2016-10-26 13:58:27 -07002021 @Override
2022 public void setClipBottomAmount(int clipBottomAmount) {
Selim Cinek65d418e2016-11-29 15:42:34 -08002023 if (clipBottomAmount != mClipBottomAmount) {
2024 super.setClipBottomAmount(clipBottomAmount);
Adrian Rooseb434ff2017-01-11 11:18:48 -08002025 for (NotificationContentView l : mLayouts) {
2026 l.setClipBottomAmount(clipBottomAmount);
2027 }
Selim Cinek65d418e2016-11-29 15:42:34 -08002028 if (mGuts != null) {
2029 mGuts.setClipBottomAmount(clipBottomAmount);
2030 }
Selim Cineka686b2c2016-10-26 13:58:27 -07002031 }
Selim Cinekb3dadcc2016-11-21 17:21:13 -08002032 if (mChildrenContainer != null) {
Selim Cinek65d418e2016-11-29 15:42:34 -08002033 // We have to update this even if it hasn't changed, since the children locations can
2034 // have changed
Selim Cinekb3dadcc2016-11-21 17:21:13 -08002035 mChildrenContainer.setClipBottomAmount(clipBottomAmount);
2036 }
Selim Cineka686b2c2016-10-26 13:58:27 -07002037 }
2038
Selim Cinek31094df2014-08-14 19:28:15 +02002039 public boolean isMaxExpandHeightInitialized() {
2040 return mMaxExpandHeight != 0;
Selim Cinek7d447722014-06-10 15:51:59 +02002041 }
Selim Cinek2f0df8a2014-06-10 17:40:42 +02002042
Selim Cinek42357e02016-02-24 18:48:01 -08002043 public NotificationContentView getShowingLayout() {
Selim Cinek2f0df8a2014-06-10 17:40:42 +02002044 return mShowingPublic ? mPublicLayout : mPrivateLayout;
2045 }
Chris Wren78403d72014-07-28 10:23:24 +01002046
Selim Cinek1a48bab2017-02-17 19:38:40 -08002047 public void setLegacy(boolean legacy) {
Adrian Rooseb434ff2017-01-11 11:18:48 -08002048 for (NotificationContentView l : mLayouts) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08002049 l.setLegacy(legacy);
Adrian Rooseb434ff2017-01-11 11:18:48 -08002050 }
Jorim Jaggi59ec3042015-06-05 15:18:43 -07002051 }
2052
Selim Cineka6c6bfb2015-10-29 16:27:08 -07002053 @Override
2054 protected void updateBackgroundTint() {
2055 super.updateBackgroundTint();
Mady Mellorb0a82462016-04-30 17:31:02 -07002056 updateBackgroundForGroupState();
Selim Cineka6c6bfb2015-10-29 16:27:08 -07002057 if (mIsSummaryWithChildren) {
2058 List<ExpandableNotificationRow> notificationChildren =
2059 mChildrenContainer.getNotificationChildren();
2060 for (int i = 0; i < notificationChildren.size(); i++) {
2061 ExpandableNotificationRow child = notificationChildren.get(i);
Mady Mellorb0a82462016-04-30 17:31:02 -07002062 child.updateBackgroundForGroupState();
Selim Cineka6c6bfb2015-10-29 16:27:08 -07002063 }
2064 }
2065 }
2066
Mady Mellorb0a82462016-04-30 17:31:02 -07002067 /**
2068 * Called when a group has finished animating from collapsed or expanded state.
2069 */
2070 public void onFinishedExpansionChange() {
2071 mGroupExpansionChanging = false;
2072 updateBackgroundForGroupState();
2073 }
2074
2075 /**
2076 * Updates the parent and children backgrounds in a group based on the expansion state.
2077 */
2078 public void updateBackgroundForGroupState() {
2079 if (mIsSummaryWithChildren) {
2080 // Only when the group has finished expanding do we hide its background.
Anthony Chen6bf88a02017-04-10 14:41:44 -07002081 mShowNoBackground = !mShowGroupBackgroundWhenExpanded && isGroupExpanded()
2082 && !isGroupExpansionChanging() && !isUserLocked();
Mady Mellorb0a82462016-04-30 17:31:02 -07002083 mChildrenContainer.updateHeaderForExpansion(mShowNoBackground);
2084 List<ExpandableNotificationRow> children = mChildrenContainer.getNotificationChildren();
2085 for (int i = 0; i < children.size(); i++) {
2086 children.get(i).updateBackgroundForGroupState();
2087 }
2088 } else if (isChildInGroup()) {
2089 final int childColor = getShowingLayout().getBackgroundColorForExpansionState();
2090 // Only show a background if the group is expanded OR if it is expanding / collapsing
Anthony Chen6bf88a02017-04-10 14:41:44 -07002091 // and has a custom background color.
Mady Mellorb0a82462016-04-30 17:31:02 -07002092 final boolean showBackground = isGroupExpanded()
2093 || ((mNotificationParent.isGroupExpansionChanging()
Anthony Chen6bf88a02017-04-10 14:41:44 -07002094 || mNotificationParent.isUserLocked()) && childColor != 0);
Mady Mellorb0a82462016-04-30 17:31:02 -07002095 mShowNoBackground = !showBackground;
2096 } else {
2097 // Only children or parents ever need no background.
2098 mShowNoBackground = false;
2099 }
2100 updateOutline();
Selim Cineka6c6bfb2015-10-29 16:27:08 -07002101 updateBackground();
2102 }
2103
Adrian Roos4a579672016-05-24 16:54:37 -07002104 public int getPositionOfChild(ExpandableNotificationRow childRow) {
2105 if (mIsSummaryWithChildren) {
2106 return mChildrenContainer.getPositionInLinearLayout(childRow);
2107 }
2108 return 0;
2109 }
2110
Chris Wren78403d72014-07-28 10:23:24 +01002111 public void setExpansionLogger(ExpansionLogger logger, String key) {
2112 mLogger = logger;
2113 mLoggingKey = key;
2114 }
2115
Chris Wren6abeeb92016-05-26 14:44:38 -04002116 public void onExpandedByGesture(boolean userExpanded) {
2117 int event = MetricsEvent.ACTION_NOTIFICATION_GESTURE_EXPANDER;
2118 if (mGroupManager.isSummaryOfGroup(getStatusBarNotification())) {
2119 event = MetricsEvent.ACTION_NOTIFICATION_GROUP_GESTURE_EXPANDER;
2120 }
2121 MetricsLogger.action(mContext, event, userExpanded);
2122 }
2123
Selim Cinek6183d122016-01-14 18:48:41 -08002124 @Override
Selim Cinek42357e02016-02-24 18:48:01 -08002125 public float getIncreasedPaddingAmount() {
2126 if (mIsSummaryWithChildren) {
2127 if (isGroupExpanded()) {
2128 return 1.0f;
2129 } else if (isUserLocked()) {
Selim Cinek414ad332017-02-24 19:06:12 -08002130 return mChildrenContainer.getIncreasedPaddingAmount();
Selim Cinek42357e02016-02-24 18:48:01 -08002131 }
Selim Cinek99104832017-01-25 14:47:33 -08002132 } else if (isColorized() && (!mIsLowPriority || isExpanded())) {
Selim Cineka7ed2c12017-01-23 20:47:24 -08002133 return -1.0f;
Selim Cinek42357e02016-02-24 18:48:01 -08002134 }
2135 return 0.0f;
Selim Cinek61633a82016-01-25 15:54:10 -08002136 }
2137
Selim Cineka7ed2c12017-01-23 20:47:24 -08002138 private boolean isColorized() {
Selim Cinek99104832017-01-25 14:47:33 -08002139 return mIsColorized && mBgTint != NO_COLOR;
Selim Cineka7ed2c12017-01-23 20:47:24 -08002140 }
2141
Selim Cinek61633a82016-01-25 15:54:10 -08002142 @Override
Selim Cinek6183d122016-01-14 18:48:41 -08002143 protected boolean disallowSingleClick(MotionEvent event) {
2144 float x = event.getX();
2145 float y = event.getY();
Selim Cinek34eda5e2016-02-18 17:10:43 -08002146 NotificationHeaderView header = getVisibleNotificationHeader();
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07002147 if (header != null && header.isInTouchRect(x - getTranslation(), y)) {
2148 return true;
2149 }
2150 if ((!mIsSummaryWithChildren || mShowingPublic)
2151 && getShowingLayout().disallowSingleClick(x, y)) {
2152 return true;
Selim Cinek6183d122016-01-14 18:48:41 -08002153 }
2154 return super.disallowSingleClick(event);
2155 }
2156
Selim Cinek414ad332017-02-24 19:06:12 -08002157 private void onExpansionChanged(boolean userAction, boolean wasExpanded) {
Chris Wren698b1702016-05-23 11:16:32 -04002158 boolean nowExpanded = isExpanded();
Selim Cinek414ad332017-02-24 19:06:12 -08002159 if (mIsSummaryWithChildren && (!mIsLowPriority || wasExpanded)) {
Chris Wren698b1702016-05-23 11:16:32 -04002160 nowExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
2161 }
Selim Cinek414ad332017-02-24 19:06:12 -08002162 if (nowExpanded != wasExpanded) {
2163 updateShelfIconColor();
2164 if (mLogger != null) {
2165 mLogger.logNotificationExpansion(mLoggingKey, userAction, nowExpanded);
2166 }
2167 if (mIsSummaryWithChildren) {
2168 mChildrenContainer.onExpansionChanged();
2169 }
Chris Wren78403d72014-07-28 10:23:24 +01002170 }
2171 }
Selim Cinek570981d2015-12-01 11:37:01 -08002172
Selim Cineke9bad242016-06-15 11:46:37 -07002173 @Override
2174 public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
2175 super.onInitializeAccessibilityNodeInfoInternal(info);
2176 if (canViewBeDismissed()) {
2177 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS);
2178 }
Selim Cinekc0ac4af2017-03-03 15:13:48 -08002179 boolean expandable = mShowingPublic;
2180 boolean isExpanded = false;
2181 if (!expandable) {
2182 if (mIsSummaryWithChildren) {
2183 expandable = true;
2184 if (!mIsLowPriority || isExpanded()) {
2185 isExpanded = isGroupExpanded();
2186 }
2187 } else {
2188 expandable = mPrivateLayout.isContentExpandable();
2189 isExpanded = isExpanded();
2190 }
2191 }
2192 if (expandable) {
2193 if (isExpanded) {
2194 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE);
2195 } else {
2196 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND);
2197 }
2198 }
Selim Cineke9bad242016-06-15 11:46:37 -07002199 }
2200
2201 @Override
2202 public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
2203 if (super.performAccessibilityActionInternal(action, arguments)) {
2204 return true;
2205 }
2206 switch (action) {
2207 case AccessibilityNodeInfo.ACTION_DISMISS:
2208 NotificationStackScrollLayout.performDismiss(this, mGroupManager,
2209 true /* fromAccessibility */);
2210 return true;
Selim Cinekc0ac4af2017-03-03 15:13:48 -08002211 case AccessibilityNodeInfo.ACTION_COLLAPSE:
2212 case AccessibilityNodeInfo.ACTION_EXPAND:
2213 mExpandClickListener.onClick(this);
2214 return true;
Selim Cineke9bad242016-06-15 11:46:37 -07002215 }
2216 return false;
2217 }
2218
2219 public boolean shouldRefocusOnDismiss() {
2220 return mRefocusOnDismiss || isAccessibilityFocused();
2221 }
2222
Selim Cinek570981d2015-12-01 11:37:01 -08002223 public interface OnExpandClickListener {
Selim Cinek31aada42015-12-18 17:51:15 -08002224 void onExpandClicked(NotificationData.Entry clickedEntry, boolean nowExpanded);
Selim Cinek570981d2015-12-01 11:37:01 -08002225 }
Selim Cinekbbcebde2016-11-09 18:28:20 -08002226
2227 @Override
2228 public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
2229 return new NotificationViewState(stackScrollState);
2230 }
2231
Selim Cinekd127d792016-11-01 19:11:41 -07002232 @Override
2233 public boolean isAboveShelf() {
Selim Cinekc8007c52017-02-28 16:09:56 -08002234 return !isOnKeyguard()
2235 && (mIsPinned || mHeadsupDisappearRunning || (mIsHeadsUp && mAboveShelf));
Selim Cinekd127d792016-11-01 19:11:41 -07002236 }
2237
Adrian Roos0aac04f2016-12-08 15:59:29 -08002238 public void setShowAmbient(boolean showAmbient) {
2239 if (showAmbient != mShowAmbient) {
2240 mShowAmbient = showAmbient;
Adrian Roos6f6e1592017-05-02 16:22:53 -07002241 if (mChildrenContainer != null) {
2242 mChildrenContainer.notifyShowAmbientChanged();
2243 }
Adrian Roos0aac04f2016-12-08 15:59:29 -08002244 notifyHeightChanged(false /* needsAnimation */);
2245 }
2246 }
2247
Adrian Roos6f6e1592017-05-02 16:22:53 -07002248 public boolean isShowingAmbient() {
2249 return mShowAmbient;
2250 }
2251
Selim Cinekd127d792016-11-01 19:11:41 -07002252 public void setAboveShelf(boolean aboveShelf) {
Selim Cinek5cf1d052017-06-01 17:36:46 -07002253 boolean wasAboveShelf = isAboveShelf();
Selim Cinekd127d792016-11-01 19:11:41 -07002254 mAboveShelf = aboveShelf;
Selim Cinek5cf1d052017-06-01 17:36:46 -07002255 if (isAboveShelf() != wasAboveShelf) {
2256 mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
2257 }
Selim Cinekd127d792016-11-01 19:11:41 -07002258 }
2259
Selim Cinekd4776a52017-02-14 18:50:16 -08002260 public static class NotificationViewState extends ExpandableViewState {
Selim Cinekbbcebde2016-11-09 18:28:20 -08002261
2262 private final StackScrollState mOverallState;
Selim Cinek0242fbb2016-10-19 13:38:32 -07002263
Selim Cinekbbcebde2016-11-09 18:28:20 -08002264
2265 private NotificationViewState(StackScrollState stackScrollState) {
2266 mOverallState = stackScrollState;
2267 }
2268
2269 @Override
2270 public void applyToView(View view) {
2271 super.applyToView(view);
2272 if (view instanceof ExpandableNotificationRow) {
2273 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekbbcebde2016-11-09 18:28:20 -08002274 row.applyChildrenState(mOverallState);
2275 }
2276 }
Selim Cinek0cfbef42016-11-09 19:06:36 -08002277
2278 @Override
Selim Cinek2b549f42016-11-22 16:38:51 -08002279 protected void onYTranslationAnimationFinished(View view) {
2280 super.onYTranslationAnimationFinished(view);
Selim Cinekd4776a52017-02-14 18:50:16 -08002281 if (view instanceof ExpandableNotificationRow) {
2282 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
2283 if (row.isHeadsUpAnimatingAway()) {
2284 row.setHeadsUpAnimatingAway(false);
2285 }
Selim Cinek0cfbef42016-11-09 19:06:36 -08002286 }
2287 }
2288
2289 @Override
2290 public void animateTo(View child, AnimationProperties properties) {
2291 super.animateTo(child, properties);
2292 if (child instanceof ExpandableNotificationRow) {
2293 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2294 row.startChildAnimation(mOverallState, properties);
2295 }
2296 }
Selim Cinekbbcebde2016-11-09 18:28:20 -08002297 }
Selim Cinek817abe72017-05-24 11:08:55 -07002298
2299 @VisibleForTesting
2300 protected void setChildrenContainer(NotificationChildrenContainer childrenContainer) {
2301 mChildrenContainer = childrenContainer;
2302 }
Chris Wren51c75102013-07-16 20:49:17 -04002303}