blob: 6c5f4b2370339ee0a23c1a005a26df5bbd49e93a [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;
Geoffrey Pitsch409db272017-08-28 14:51:52 +000039import android.view.KeyEvent;
Selim Cinek01af3342016-02-09 19:25:31 -080040import android.view.LayoutInflater;
Selim Cinek6183d122016-01-14 18:48:41 -080041import android.view.MotionEvent;
Selim Cinekeaa29ca2015-11-23 13:51:13 -080042import android.view.NotificationHeaderView;
Dan Sandlera5e0f412014-01-23 15:11:54 -050043import android.view.View;
Selim Cinek875a3a12016-11-18 17:52:16 -080044import android.view.ViewGroup;
Jorim Jaggib1cd3c12014-09-08 19:55:17 +020045import android.view.ViewStub;
Jorim Jaggife40f7d2014-04-28 15:20:04 +020046import android.view.accessibility.AccessibilityEvent;
Selim Cineke9bad242016-06-15 11:46:37 -070047import android.view.accessibility.AccessibilityNodeInfo;
Selim Cinek98713a42015-09-21 15:47:20 +020048import android.widget.Chronometer;
Mady Mellor95d743c2017-01-10 12:05:27 -080049import android.widget.FrameLayout;
Selim Cinekcab4a602014-09-03 14:47:57 +020050import android.widget.ImageView;
Selim Cinek1a48bab2017-02-17 19:38:40 -080051import android.widget.RemoteViews;
Selim Cinekb5605e52015-02-20 18:21:41 +010052
Selim Cinek5ba22542017-04-20 15:16:10 -070053import com.android.internal.annotations.VisibleForTesting;
Chris Wren698b1702016-05-23 11:16:32 -040054import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010055import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Selim Cinek4bb59342016-04-08 19:29:35 -070056import com.android.internal.util.NotificationColorUtil;
Selim Cinek875a3a12016-11-18 17:52:16 -080057import com.android.internal.widget.CachingIconView;
Mady Mellor95d743c2017-01-10 12:05:27 -080058import com.android.systemui.Dependency;
Selim Cinek0242fbb2016-10-19 13:38:32 -070059import com.android.systemui.Interpolators;
Dan Sandlera5e0f412014-01-23 15:11:54 -050060import com.android.systemui.R;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070061import com.android.systemui.classifier.FalsingManager;
Mady Mellor95d743c2017-01-10 12:05:27 -080062import com.android.systemui.plugins.PluginListener;
63import com.android.systemui.plugins.PluginManager;
64import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
65import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
66import com.android.systemui.statusbar.NotificationGuts.GutsContent;
Selim Cinek5cf1d052017-06-01 17:36:46 -070067import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
Selim Cineka6ee4bd2017-09-20 22:52:29 +000068import com.android.systemui.statusbar.notification.AboveShelfObserver;
Selim Cinekc897bd32016-03-18 17:32:31 -070069import com.android.systemui.statusbar.notification.HybridNotificationView;
Selim Cinek1a48bab2017-02-17 19:38:40 -080070import com.android.systemui.statusbar.notification.NotificationInflater;
Selim Cinek6743c0b2017-01-18 18:24:01 -080071import com.android.systemui.statusbar.notification.NotificationUtils;
Selim Cineka7d4f822016-12-06 14:34:47 -080072import com.android.systemui.statusbar.notification.VisualStabilityManager;
Selim Cinekb5605e52015-02-20 18:21:41 +010073import com.android.systemui.statusbar.phone.NotificationGroupManager;
Jason Monk2a6ea9c2017-01-26 11:14:51 -050074import com.android.systemui.statusbar.phone.StatusBar;
Selim Cinek31aada42015-12-18 17:51:15 -080075import com.android.systemui.statusbar.policy.HeadsUpManager;
Selim Cinek0cfbef42016-11-09 19:06:36 -080076import com.android.systemui.statusbar.stack.AnimationProperties;
77import com.android.systemui.statusbar.stack.ExpandableViewState;
Selim Cinekb5605e52015-02-20 18:21:41 +010078import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
Selim Cineke9bad242016-06-15 11:46:37 -070079import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
Selim Cinekb5605e52015-02-20 18:21:41 +010080import com.android.systemui.statusbar.stack.StackScrollState;
Selim Cinekb5605e52015-02-20 18:21:41 +010081
Mady Mellor4b80b102016-01-22 08:03:58 -080082import java.util.ArrayList;
Selim Cinekb5605e52015-02-20 18:21:41 +010083import java.util.List;
Lucas Dupincecc7c22017-09-12 16:02:45 -070084import java.util.function.BooleanSupplier;
Dan Sandlera5e0f412014-01-23 15:11:54 -050085
Mady Mellor95d743c2017-01-10 12:05:27 -080086public class ExpandableNotificationRow extends ActivatableNotificationView
87 implements PluginListener<NotificationMenuRowPlugin> {
Selim Cinekb5605e52015-02-20 18:21:41 +010088
89 private static final int DEFAULT_DIVIDER_ALPHA = 0x29;
90 private static final int COLORED_DIVIDER_ALPHA = 0x7B;
Mady Mellor95d743c2017-01-10 12:05:27 -080091 private static final int MENU_VIEW_INDEX = 0;
92
Mady Mellor035badd2017-04-04 18:45:30 -070093 public interface LayoutListener {
94 public void onLayout();
95 }
96
97 private LayoutListener mLayoutListener;
Lucas Dupincecc7c22017-09-12 16:02:45 -070098 private boolean mDark;
Selim Cinek55a3e732017-05-25 18:30:10 -070099 private boolean mLowPriorityStateUpdated;
Selim Cinek1a48bab2017-02-17 19:38:40 -0800100 private final NotificationInflater mNotificationInflater;
Selim Cinek0242fbb2016-10-19 13:38:32 -0700101 private int mIconTransformContentShift;
Selim Cinek875a3a12016-11-18 17:52:16 -0800102 private int mIconTransformContentShiftNoIcon;
Selim Cinek01af3342016-02-09 19:25:31 -0800103 private int mNotificationMinHeightLegacy;
104 private int mMaxHeadsUpHeightLegacy;
105 private int mMaxHeadsUpHeight;
Selim Cinek87ed69b2017-02-09 15:59:43 -0800106 private int mMaxHeadsUpHeightIncreased;
Selim Cinek01af3342016-02-09 19:25:31 -0800107 private int mNotificationMinHeight;
Selim Cinek7d1009b2017-01-25 15:28:28 -0800108 private int mNotificationMinHeightLarge;
Selim Cinek01af3342016-02-09 19:25:31 -0800109 private int mNotificationMaxHeight;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800110 private int mNotificationAmbientHeight;
Mady Mellorb0a82462016-04-30 17:31:02 -0700111 private int mIncreasedPaddingBetweenElements;
Chris Wren51c75102013-07-16 20:49:17 -0400112
Selim Cinek1685e632014-04-08 02:27:49 +0200113 /** Does this row contain layouts that can adapt to row expansion */
Chris Wren51c75102013-07-16 20:49:17 -0400114 private boolean mExpandable;
Selim Cinek1685e632014-04-08 02:27:49 +0200115 /** Has the user actively changed the expansion state of this row */
116 private boolean mHasUserChangedExpansion;
117 /** If {@link #mHasUserChangedExpansion}, has the user expanded this row */
Chris Wren51c75102013-07-16 20:49:17 -0400118 private boolean mUserExpanded;
Selim Cinek31aada42015-12-18 17:51:15 -0800119
120 /**
121 * Has this notification been expanded while it was pinned
122 */
123 private boolean mExpandedWhenPinned;
Selim Cinek1685e632014-04-08 02:27:49 +0200124 /** Is the user touching this row */
Chris Wren51c75102013-07-16 20:49:17 -0400125 private boolean mUserLocked;
Selim Cinek1685e632014-04-08 02:27:49 +0200126 /** Are we showing the "public" version */
Dan Sandlera5e0f412014-01-23 15:11:54 -0500127 private boolean mShowingPublic;
Jorim Jaggiae441282014-08-01 02:45:18 +0200128 private boolean mSensitive;
Selim Cinek3c76d502016-02-19 15:16:33 -0800129 private boolean mSensitiveHiddenInGeneral;
Jorim Jaggiae441282014-08-01 02:45:18 +0200130 private boolean mShowingPublicInitialized;
Selim Cinek60122be2015-04-15 18:16:50 -0700131 private boolean mHideSensitiveForIntrinsicHeight;
Chris Wren51c75102013-07-16 20:49:17 -0400132
Selim Cinek1685e632014-04-08 02:27:49 +0200133 /**
134 * Is this notification expanded by the system. The expansion state can be overridden by the
135 * user expansion.
136 */
137 private boolean mIsSystemExpanded;
Jorim Jaggidce3c4c2014-04-29 23:12:24 +0200138
139 /**
Selim Cinek83bc7832015-10-22 13:26:54 -0700140 * Whether the notification is on the keyguard and the expansion is disabled.
Jorim Jaggidce3c4c2014-04-29 23:12:24 +0200141 */
Selim Cinek83bc7832015-10-22 13:26:54 -0700142 private boolean mOnKeyguard;
Jorim Jaggidce3c4c2014-04-29 23:12:24 +0200143
Mady Mellorb0a82462016-04-30 17:31:02 -0700144 private Animator mTranslateAnim;
Mady Mellor4b80b102016-01-22 08:03:58 -0800145 private ArrayList<View> mTranslateableViews;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200146 private NotificationContentView mPublicLayout;
147 private NotificationContentView mPrivateLayout;
Adrian Rooseb434ff2017-01-11 11:18:48 -0800148 private NotificationContentView[] mLayouts;
Selim Cinek1685e632014-04-08 02:27:49 +0200149 private int mMaxExpandHeight;
Selim Cinek8d490d42015-04-10 00:05:50 -0700150 private int mHeadsUpHeight;
Selim Cinek4bb59342016-04-08 19:29:35 -0700151 private int mNotificationColor;
Chris Wren78403d72014-07-28 10:23:24 +0100152 private ExpansionLogger mLogger;
153 private String mLoggingKey;
Selim Cinek8d490d42015-04-10 00:05:50 -0700154 private NotificationGuts mGuts;
Selim Cinekda42d652015-12-04 15:51:16 -0800155 private NotificationData.Entry mEntry;
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200156 private StatusBarNotification mStatusBarNotification;
Mady Mellor3fd273e2016-03-15 21:08:14 -0700157 private String mAppName;
Selim Cinek1a521f32014-11-03 17:39:29 +0100158 private boolean mIsHeadsUp;
Selim Cinek98713a42015-09-21 15:47:20 +0200159 private boolean mLastChronometerRunning = true;
Selim Cinekb5605e52015-02-20 18:21:41 +0100160 private ViewStub mChildrenContainerStub;
161 private NotificationGroupManager mGroupManager;
Selim Cinekb5605e52015-02-20 18:21:41 +0100162 private boolean mChildrenExpanded;
Selim Cinek263398f2015-10-21 17:40:23 -0700163 private boolean mIsSummaryWithChildren;
Selim Cinekb5605e52015-02-20 18:21:41 +0100164 private NotificationChildrenContainer mChildrenContainer;
Mady Mellor95d743c2017-01-10 12:05:27 -0800165 private NotificationMenuRowPlugin mMenuRow;
Selim Cinekab29aeb2015-02-20 18:18:32 +0100166 private ViewStub mGutsStub;
Selim Cinekb5605e52015-02-20 18:21:41 +0100167 private boolean mIsSystemChildExpanded;
Selim Cinek684a4422015-04-15 16:18:39 -0700168 private boolean mIsPinned;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700169 private FalsingManager mFalsingManager;
Selim Cinek5cf1d052017-06-01 17:36:46 -0700170 private AboveShelfChangedListener mAboveShelfChangedListener;
Selim Cinek31aada42015-12-18 17:51:15 -0800171 private HeadsUpManager mHeadsUpManager;
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200172
Jorim Jaggi5eb67c22015-08-19 19:50:49 -0700173 private boolean mJustClicked;
Selim Cinek5a175d92015-11-23 18:01:33 -0800174 private boolean mIconAnimationRunning;
Selim Cinek34d93b02015-10-22 12:30:38 -0700175 private boolean mShowNoBackground;
Selim Cinek388df6d2015-10-22 13:25:11 -0700176 private ExpandableNotificationRow mNotificationParent;
Selim Cinek570981d2015-12-01 11:37:01 -0800177 private OnExpandClickListener mOnExpandClickListener;
Geoffrey Pitsch409db272017-08-28 14:51:52 +0000178
179 // Listener will be called when receiving a long click event.
180 // Use #setLongPressPosition to optionally assign positional data with the long press.
181 private LongPressListener mLongPressListener;
182
Mady Mellorb0a82462016-04-30 17:31:02 -0700183 private boolean mGroupExpansionChanging;
184
Anthony Chen6bf88a02017-04-10 14:41:44 -0700185 /**
Lucas Dupincecc7c22017-09-12 16:02:45 -0700186 * A supplier that returns true if keyguard is secure.
187 */
188 private BooleanSupplier mSecureStateProvider;
189
190 /**
Anthony Chen6bf88a02017-04-10 14:41:44 -0700191 * Whether or not a notification that is not part of a group of notifications can be manually
192 * expanded by the user.
193 */
194 private boolean mEnableNonGroupedNotificationExpand;
195
196 /**
197 * Whether or not to update the background of the header of the notification when its expanded.
198 * If {@code true}, the header background will disappear when expanded.
199 */
200 private boolean mShowGroupBackgroundWhenExpanded;
201
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800202 private OnClickListener mExpandClickListener = new OnClickListener() {
203 @Override
204 public void onClick(View v) {
Selim Cinek414ad332017-02-24 19:06:12 -0800205 if (!mShowingPublic && (!mIsLowPriority || isExpanded())
206 && mGroupManager.isSummaryOfGroup(mStatusBarNotification)) {
Mady Mellor1a5d8ea2016-06-09 10:42:42 -0700207 mGroupExpansionChanging = true;
Chris Wren698b1702016-05-23 11:16:32 -0400208 final boolean wasExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
209 boolean nowExpanded = mGroupManager.toggleGroupExpansion(mStatusBarNotification);
210 mOnExpandClickListener.onExpandClicked(mEntry, nowExpanded);
Chris Wren698b1702016-05-23 11:16:32 -0400211 MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTIFICATION_GROUP_EXPANDER,
212 nowExpanded);
Selim Cinek414ad332017-02-24 19:06:12 -0800213 onExpansionChanged(true /* userAction */, wasExpanded);
Anthony Chen6bf88a02017-04-10 14:41:44 -0700214 } else if (mEnableNonGroupedNotificationExpand) {
Selim Cineke9bad242016-06-15 11:46:37 -0700215 if (v.isAccessibilityFocused()) {
216 mPrivateLayout.setFocusOnVisibilityChange();
217 }
Selim Cinek31aada42015-12-18 17:51:15 -0800218 boolean nowExpanded;
219 if (isPinned()) {
220 nowExpanded = !mExpandedWhenPinned;
221 mExpandedWhenPinned = nowExpanded;
222 } else {
223 nowExpanded = !isExpanded();
224 setUserExpanded(nowExpanded);
225 }
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800226 notifyHeightChanged(true);
Selim Cinek31aada42015-12-18 17:51:15 -0800227 mOnExpandClickListener.onExpandClicked(mEntry, nowExpanded);
Chris Wren698b1702016-05-23 11:16:32 -0400228 MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTIFICATION_EXPANDER,
229 nowExpanded);
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800230 }
231 }
232 };
Selim Cinek1b2a05e2016-04-28 14:20:39 -0700233 private boolean mForceUnlocked;
Selim Cinek3f19f602016-05-02 18:01:56 -0700234 private boolean mDismissed;
235 private boolean mKeepInParent;
236 private boolean mRemoved;
Mady Mellorb0a82462016-04-30 17:31:02 -0700237 private static final Property<ExpandableNotificationRow, Float> TRANSLATE_CONTENT =
238 new FloatProperty<ExpandableNotificationRow>("translate") {
239 @Override
240 public void setValue(ExpandableNotificationRow object, float value) {
241 object.setTranslation(value);
242 }
243
244 @Override
245 public Float get(ExpandableNotificationRow object) {
246 return object.getTranslation();
247 }
248 };
Selim Cinekddf1b392016-05-27 16:33:10 -0700249 private OnClickListener mOnClickListener;
Selim Cinek73cf02a2016-06-17 13:08:00 -0700250 private boolean mHeadsupDisappearRunning;
Selim Cineke9bad242016-06-15 11:46:37 -0700251 private View mChildAfterViewWhenDismissed;
252 private View mGroupParentWhenDismissed;
253 private boolean mRefocusOnDismiss;
Selim Cinek2b549f42016-11-22 16:38:51 -0800254 private float mContentTransformationAmount;
Selim Cinek0242fbb2016-10-19 13:38:32 -0700255 private boolean mIconsVisible = true;
Selim Cinekd127d792016-11-01 19:11:41 -0700256 private boolean mAboveShelf;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800257 private boolean mShowAmbient;
Selim Cinek875a3a12016-11-18 17:52:16 -0800258 private boolean mIsLastChild;
Selim Cineke9079112016-12-14 14:41:01 -0800259 private Runnable mOnDismissRunnable;
Selim Cinek6743c0b2017-01-18 18:24:01 -0800260 private boolean mIsLowPriority;
Selim Cineka7ed2c12017-01-23 20:47:24 -0800261 private boolean mIsColorized;
Selim Cinek7d1009b2017-01-25 15:28:28 -0800262 private boolean mUseIncreasedCollapsedHeight;
Selim Cinek87ed69b2017-02-09 15:59:43 -0800263 private boolean mUseIncreasedHeadsUpHeight;
Selim Cinekef8c2252017-02-10 14:52:18 -0800264 private float mTranslationWhenRemoved;
265 private boolean mWasChildInGroupWhenRemoved;
Adrian Roos6f6e1592017-05-02 16:22:53 -0700266 private int mNotificationColorAmbient;
Mady Mellorb0a82462016-04-30 17:31:02 -0700267
Mady Mellor43c2cd12016-12-12 21:05:13 -0800268 @Override
Mady Mellorb0a82462016-04-30 17:31:02 -0700269 public boolean isGroupExpansionChanging() {
270 if (isChildInGroup()) {
271 return mNotificationParent.isGroupExpansionChanging();
272 }
273 return mGroupExpansionChanging;
274 }
275
276 public void setGroupExpansionChanging(boolean changing) {
277 mGroupExpansionChanging = changing;
278 }
Jorim Jaggi5eb67c22015-08-19 19:50:49 -0700279
Adrian Roos599be342016-06-13 14:54:39 -0700280 @Override
281 public void setActualHeightAnimating(boolean animating) {
282 if (mPrivateLayout != null) {
283 mPrivateLayout.setContentHeightAnimating(animating);
284 }
285 }
286
Selim Cinek8d490d42015-04-10 00:05:50 -0700287 public NotificationContentView getPrivateLayout() {
288 return mPrivateLayout;
289 }
290
291 public NotificationContentView getPublicLayout() {
292 return mPublicLayout;
293 }
294
Selim Cinekcab4a602014-09-03 14:47:57 +0200295 public void setIconAnimationRunning(boolean running) {
Adrian Rooseb434ff2017-01-11 11:18:48 -0800296 for (NotificationContentView l : mLayouts) {
297 setIconAnimationRunning(running, l);
298 }
Selim Cinek5a175d92015-11-23 18:01:33 -0800299 if (mIsSummaryWithChildren) {
Mady Mellorb0a82462016-04-30 17:31:02 -0700300 setIconAnimationRunningForChild(running, mChildrenContainer.getHeaderView());
Selim Cinek414ad332017-02-24 19:06:12 -0800301 setIconAnimationRunningForChild(running, mChildrenContainer.getLowPriorityHeaderView());
Selim Cinek5a175d92015-11-23 18:01:33 -0800302 List<ExpandableNotificationRow> notificationChildren =
303 mChildrenContainer.getNotificationChildren();
304 for (int i = 0; i < notificationChildren.size(); i++) {
305 ExpandableNotificationRow child = notificationChildren.get(i);
306 child.setIconAnimationRunning(running);
307 }
308 }
309 mIconAnimationRunning = running;
Selim Cinekcab4a602014-09-03 14:47:57 +0200310 }
311
312 private void setIconAnimationRunning(boolean running, NotificationContentView layout) {
313 if (layout != null) {
314 View contractedChild = layout.getContractedChild();
315 View expandedChild = layout.getExpandedChild();
Selim Cinek8d490d42015-04-10 00:05:50 -0700316 View headsUpChild = layout.getHeadsUpChild();
Selim Cinekcab4a602014-09-03 14:47:57 +0200317 setIconAnimationRunningForChild(running, contractedChild);
318 setIconAnimationRunningForChild(running, expandedChild);
Selim Cinek8d490d42015-04-10 00:05:50 -0700319 setIconAnimationRunningForChild(running, headsUpChild);
Selim Cinekcab4a602014-09-03 14:47:57 +0200320 }
321 }
322
323 private void setIconAnimationRunningForChild(boolean running, View child) {
324 if (child != null) {
325 ImageView icon = (ImageView) child.findViewById(com.android.internal.R.id.icon);
326 setIconRunning(icon, running);
327 ImageView rightIcon = (ImageView) child.findViewById(
328 com.android.internal.R.id.right_icon);
329 setIconRunning(rightIcon, running);
330 }
331 }
332
333 private void setIconRunning(ImageView imageView, boolean running) {
334 if (imageView != null) {
335 Drawable drawable = imageView.getDrawable();
336 if (drawable instanceof AnimationDrawable) {
337 AnimationDrawable animationDrawable = (AnimationDrawable) drawable;
338 if (running) {
339 animationDrawable.start();
340 } else {
341 animationDrawable.stop();
342 }
343 } else if (drawable instanceof AnimatedVectorDrawable) {
344 AnimatedVectorDrawable animationDrawable = (AnimatedVectorDrawable) drawable;
345 if (running) {
346 animationDrawable.start();
347 } else {
348 animationDrawable.stop();
349 }
350 }
351 }
352 }
353
Selim Cinek5ba22542017-04-20 15:16:10 -0700354 public void updateNotification(NotificationData.Entry entry) {
Selim Cinekda42d652015-12-04 15:51:16 -0800355 mEntry = entry;
356 mStatusBarNotification = entry.notification;
Selim Cinek1a48bab2017-02-17 19:38:40 -0800357 mNotificationInflater.inflateNotificationViews();
Selim Cinekc478f902017-02-22 20:55:44 -0800358 }
359
Selim Cinek5ba22542017-04-20 15:16:10 -0700360 public void onNotificationUpdated() {
Adrian Rooseb434ff2017-01-11 11:18:48 -0800361 for (NotificationContentView l : mLayouts) {
Selim Cinekc478f902017-02-22 20:55:44 -0800362 l.onNotificationUpdated(mEntry);
Adrian Rooseb434ff2017-01-11 11:18:48 -0800363 }
Selim Cineka7ed2c12017-01-23 20:47:24 -0800364 mIsColorized = mStatusBarNotification.getNotification().isColorized();
Selim Cinek757d8792016-01-28 16:21:08 -0800365 mShowingPublicInitialized = false;
Selim Cinek4bb59342016-04-08 19:29:35 -0700366 updateNotificationColor();
Mady Mellor4c197602017-04-10 17:57:52 -0700367 if (mMenuRow != null) {
Mady Mellor4ab28202017-06-06 11:42:50 -0700368 mMenuRow.onNotificationUpdated(mStatusBarNotification);
Mady Mellor4c197602017-04-10 17:57:52 -0700369 }
Selim Cinek8fc93c92015-11-23 17:48:07 -0800370 if (mIsSummaryWithChildren) {
Selim Cinek414ad332017-02-24 19:06:12 -0800371 mChildrenContainer.recreateNotificationHeader(mExpandClickListener);
Selim Cinekc897bd32016-03-18 17:32:31 -0700372 mChildrenContainer.onNotificationUpdated();
Selim Cinek8fc93c92015-11-23 17:48:07 -0800373 }
Selim Cinek5a175d92015-11-23 18:01:33 -0800374 if (mIconAnimationRunning) {
375 setIconAnimationRunning(true);
376 }
Selim Cinekea4bef72015-12-02 15:51:10 -0800377 if (mNotificationParent != null) {
378 mNotificationParent.updateChildrenHeaderAppearance();
379 }
Selim Cinek263398f2015-10-21 17:40:23 -0700380 onChildrenCountChanged();
Selim Cinek624c02db2015-12-14 21:00:02 -0800381 // The public layouts expand button is always visible
382 mPublicLayout.updateExpandButtons(true);
Selim Cinekda42d652015-12-04 15:51:16 -0800383 updateLimits();
Selim Cinek0242fbb2016-10-19 13:38:32 -0700384 updateIconVisibilities();
Selim Cinek6743c0b2017-01-18 18:24:01 -0800385 updateShelfIconColor();
386 }
387
Lucas Dupinb6ed63b2017-05-30 16:17:42 -0700388 @VisibleForTesting
389 void updateShelfIconColor() {
Selim Cinek6743c0b2017-01-18 18:24:01 -0800390 StatusBarIconView expandedIcon = mEntry.expandedIcon;
391 boolean isPreL = Boolean.TRUE.equals(expandedIcon.getTag(R.id.icon_is_pre_L));
392 boolean colorize = !isPreL || NotificationUtils.isGrayscale(expandedIcon,
393 NotificationColorUtil.getInstance(mContext));
Selim Cinek875ba9b2017-02-13 16:20:17 -0800394 int color = StatusBarIconView.NO_COLOR;
Selim Cinek6743c0b2017-01-18 18:24:01 -0800395 if (colorize) {
Lucas Dupinb6ed63b2017-05-30 16:17:42 -0700396 NotificationHeaderView header = getVisibleNotificationHeader();
397 if (header != null) {
398 color = header.getOriginalIconColor();
399 } else {
400 color = mEntry.getContrastedColor(mContext, mIsLowPriority && !isExpanded(),
401 getBackgroundColorWithoutTint());
402 }
Selim Cinek6743c0b2017-01-18 18:24:01 -0800403 }
Selim Cinek875ba9b2017-02-13 16:20:17 -0800404 expandedIcon.setStaticDrawableColor(color);
Selim Cinekda42d652015-12-04 15:51:16 -0800405 }
406
Selim Cinek5cf1d052017-06-01 17:36:46 -0700407 public void setAboveShelfChangedListener(AboveShelfChangedListener aboveShelfChangedListener) {
408 mAboveShelfChangedListener = aboveShelfChangedListener;
409 }
410
Lucas Dupincecc7c22017-09-12 16:02:45 -0700411 /**
412 * Sets a supplier that can determine whether the keyguard is secure or not.
413 * @param secureStateProvider A function that returns true if keyguard is secure.
414 */
415 public void setSecureStateProvider(BooleanSupplier secureStateProvider) {
416 mSecureStateProvider = secureStateProvider;
417 }
418
Selim Cinek4705f292017-04-24 22:18:48 -0700419 @Override
420 public boolean isDimmable() {
421 if (!getShowingLayout().isDimmable()) {
422 return false;
423 }
424 return super.isDimmable();
425 }
426
Selim Cinekda42d652015-12-04 15:51:16 -0800427 private void updateLimits() {
Adrian Rooseb434ff2017-01-11 11:18:48 -0800428 for (NotificationContentView l : mLayouts) {
429 updateLimitsForView(l);
430 }
Selim Cineka1744872016-03-11 15:36:06 -0800431 }
432
433 private void updateLimitsForView(NotificationContentView layout) {
434 boolean customView = layout.getContractedChild().getId()
Selim Cinekda42d652015-12-04 15:51:16 -0800435 != com.android.internal.R.id.status_bar_latest_event_content;
436 boolean beforeN = mEntry.targetSdk < Build.VERSION_CODES.N;
Selim Cinek7d1009b2017-01-25 15:28:28 -0800437 int minHeight;
438 if (customView && beforeN && !mIsSummaryWithChildren) {
439 minHeight = mNotificationMinHeightLegacy;
440 } else if (mUseIncreasedCollapsedHeight && layout == mPrivateLayout) {
441 minHeight = mNotificationMinHeightLarge;
442 } else {
443 minHeight = mNotificationMinHeight;
444 }
Selim Cineka1744872016-03-11 15:36:06 -0800445 boolean headsUpCustom = layout.getHeadsUpChild() != null &&
446 layout.getHeadsUpChild().getId()
447 != com.android.internal.R.id.status_bar_latest_event_content;
Selim Cinek87ed69b2017-02-09 15:59:43 -0800448 int headsUpheight;
449 if (headsUpCustom && beforeN) {
450 headsUpheight = mMaxHeadsUpHeightLegacy;
451 } else if (mUseIncreasedHeadsUpHeight && layout == mPrivateLayout) {
452 headsUpheight = mMaxHeadsUpHeightIncreased;
453 } else {
454 headsUpheight = mMaxHeadsUpHeight;
455 }
Adrian Roos0aac04f2016-12-08 15:59:29 -0800456 layout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight,
457 mNotificationAmbientHeight);
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200458 }
459
460 public StatusBarNotification getStatusBarNotification() {
461 return mStatusBarNotification;
462 }
463
Selim Cinek281c2022016-10-13 19:14:43 -0700464 public NotificationData.Entry getEntry() {
465 return mEntry;
466 }
467
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700468 public boolean isHeadsUp() {
469 return mIsHeadsUp;
470 }
471
Selim Cinek1a521f32014-11-03 17:39:29 +0100472 public void setHeadsUp(boolean isHeadsUp) {
Selim Cinek5cf1d052017-06-01 17:36:46 -0700473 boolean wasAboveShelf = isAboveShelf();
Selim Cinekc80fdb12015-04-13 15:09:08 -0700474 int intrinsicBefore = getIntrinsicHeight();
Selim Cinek1a521f32014-11-03 17:39:29 +0100475 mIsHeadsUp = isHeadsUp;
Selim Cinek8d490d42015-04-10 00:05:50 -0700476 mPrivateLayout.setHeadsUp(isHeadsUp);
Selim Cinekb41b2f62016-04-26 14:03:29 -0700477 if (mIsSummaryWithChildren) {
478 // The overflow might change since we allow more lines as HUN.
479 mChildrenContainer.updateGroupOverflow();
480 }
Selim Cinekc80fdb12015-04-13 15:09:08 -0700481 if (intrinsicBefore != getIntrinsicHeight()) {
482 notifyHeightChanged(false /* needsAnimation */);
483 }
Selim Cinekd127d792016-11-01 19:11:41 -0700484 if (isHeadsUp) {
485 setAboveShelf(true);
Selim Cinek5cf1d052017-06-01 17:36:46 -0700486 } else if (isAboveShelf() != wasAboveShelf) {
487 mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
Selim Cinekd127d792016-11-01 19:11:41 -0700488 }
Selim Cinek1a521f32014-11-03 17:39:29 +0100489 }
490
Selim Cinekb5605e52015-02-20 18:21:41 +0100491 public void setGroupManager(NotificationGroupManager groupManager) {
492 mGroupManager = groupManager;
Selim Cinek83bc7832015-10-22 13:26:54 -0700493 mPrivateLayout.setGroupManager(groupManager);
Selim Cinekb5605e52015-02-20 18:21:41 +0100494 }
495
Adrian Roosb88b1a12015-12-09 18:51:05 -0800496 public void setRemoteInputController(RemoteInputController r) {
497 mPrivateLayout.setRemoteInputController(r);
498 }
499
Mady Mellor3fd273e2016-03-15 21:08:14 -0700500 public void setAppName(String appName) {
501 mAppName = appName;
Mady Mellor95d743c2017-01-10 12:05:27 -0800502 if (mMenuRow != null && mMenuRow.getMenuView() != null) {
Mady Mellor761cde12017-01-10 11:36:39 -0800503 mMenuRow.setAppName(mAppName);
Mady Mellor3fd273e2016-03-15 21:08:14 -0700504 }
505 }
506
Selim Cinekb5605e52015-02-20 18:21:41 +0100507 public void addChildNotification(ExpandableNotificationRow row) {
508 addChildNotification(row, -1);
509 }
510
511 /**
512 * Add a child notification to this view.
513 *
514 * @param row the row to add
515 * @param childIndex the index to add it at, if -1 it will be added at the end
516 */
517 public void addChildNotification(ExpandableNotificationRow row, int childIndex) {
518 if (mChildrenContainer == null) {
519 mChildrenContainerStub.inflate();
520 }
521 mChildrenContainer.addNotification(row, childIndex);
Selim Cinek263398f2015-10-21 17:40:23 -0700522 onChildrenCountChanged();
523 row.setIsChildInGroup(true, this);
Selim Cinekb5605e52015-02-20 18:21:41 +0100524 }
525
526 public void removeChildNotification(ExpandableNotificationRow row) {
527 if (mChildrenContainer != null) {
528 mChildrenContainer.removeNotification(row);
529 }
Selim Cinek263398f2015-10-21 17:40:23 -0700530 onChildrenCountChanged();
531 row.setIsChildInGroup(false, null);
532 }
533
Mady Mellor43c2cd12016-12-12 21:05:13 -0800534 @Override
Selim Cinek263398f2015-10-21 17:40:23 -0700535 public boolean isChildInGroup() {
Selim Cineka6c6bfb2015-10-29 16:27:08 -0700536 return mNotificationParent != null;
Selim Cinek263398f2015-10-21 17:40:23 -0700537 }
538
Selim Cinek388df6d2015-10-22 13:25:11 -0700539 public ExpandableNotificationRow getNotificationParent() {
540 return mNotificationParent;
541 }
542
Selim Cinek263398f2015-10-21 17:40:23 -0700543 /**
544 * @param isChildInGroup Is this notification now in a group
545 * @param parent the new parent notification
546 */
Selim Cineka6c6bfb2015-10-29 16:27:08 -0700547 public void setIsChildInGroup(boolean isChildInGroup, ExpandableNotificationRow parent) {;
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500548 boolean childInGroup = StatusBar.ENABLE_CHILD_NOTIFICATIONS && isChildInGroup;
Selim Cineka6c6bfb2015-10-29 16:27:08 -0700549 mNotificationParent = childInGroup ? parent : null;
550 mPrivateLayout.setIsChildInGroup(childInGroup);
Selim Cinek5ba22542017-04-20 15:16:10 -0700551 mNotificationInflater.setIsChildInGroup(childInGroup);
Mady Mellorc7d65b42016-05-04 11:44:57 -0400552 resetBackgroundAlpha();
Mady Mellorb0a82462016-04-30 17:31:02 -0700553 updateBackgroundForGroupState();
Selim Cinekddf1b392016-05-27 16:33:10 -0700554 updateClickAndFocus();
Mady Mellorb0a82462016-04-30 17:31:02 -0700555 if (mNotificationParent != null) {
Selim Cinek9ce32852017-02-15 16:21:10 -0800556 setOverrideTintColor(NO_COLOR, 0.0f);
Mady Mellorb0a82462016-04-30 17:31:02 -0700557 mNotificationParent.updateBackgroundForGroupState();
558 }
Selim Cinekdb167372016-11-17 15:41:17 -0800559 updateIconVisibilities();
Selim Cinek34d93b02015-10-22 12:30:38 -0700560 }
561
562 @Override
Selim Cinek72109472016-01-15 16:33:22 -0800563 public boolean onTouchEvent(MotionEvent event) {
564 if (event.getActionMasked() != MotionEvent.ACTION_DOWN
565 || !isChildInGroup() || isGroupExpanded()) {
566 return super.onTouchEvent(event);
567 } else {
568 return false;
569 }
570 }
571
572 @Override
Mady Mellorf0625802016-02-11 18:03:48 -0800573 protected boolean handleSlideBack() {
Mady Mellor95d743c2017-01-10 12:05:27 -0800574 if (mMenuRow != null && mMenuRow.isMenuVisible()) {
Mady Mellorf0625802016-02-11 18:03:48 -0800575 animateTranslateNotification(0 /* targetLeft */);
576 return true;
577 }
578 return false;
579 }
580
581 @Override
Selim Cinek34d93b02015-10-22 12:30:38 -0700582 protected boolean shouldHideBackground() {
583 return super.shouldHideBackground() || mShowNoBackground;
Selim Cinek263398f2015-10-21 17:40:23 -0700584 }
585
586 @Override
587 public boolean isSummaryWithChildren() {
588 return mIsSummaryWithChildren;
Selim Cinekb5605e52015-02-20 18:21:41 +0100589 }
590
591 @Override
592 public boolean areChildrenExpanded() {
593 return mChildrenExpanded;
594 }
595
596 public List<ExpandableNotificationRow> getNotificationChildren() {
597 return mChildrenContainer == null ? null : mChildrenContainer.getNotificationChildren();
598 }
599
Selim Cinekeef84282015-10-30 16:28:00 -0700600 public int getNumberOfNotificationChildren() {
601 if (mChildrenContainer == null) {
602 return 0;
603 }
604 return mChildrenContainer.getNotificationChildren().size();
605 }
606
Selim Cinekb5605e52015-02-20 18:21:41 +0100607 /**
608 * Apply the order given in the list to the children.
609 *
610 * @param childOrder the new list order
Selim Cineka7d4f822016-12-06 14:34:47 -0800611 * @param visualStabilityManager
612 * @param callback the callback to invoked in case it is not allowed
Selim Cinekb5605e52015-02-20 18:21:41 +0100613 * @return whether the list order has changed
614 */
Selim Cineka7d4f822016-12-06 14:34:47 -0800615 public boolean applyChildOrder(List<ExpandableNotificationRow> childOrder,
616 VisualStabilityManager visualStabilityManager,
617 VisualStabilityManager.Callback callback) {
618 return mChildrenContainer != null && mChildrenContainer.applyChildOrder(childOrder,
619 visualStabilityManager, callback);
Selim Cinekb5605e52015-02-20 18:21:41 +0100620 }
621
622 public void getChildrenStates(StackScrollState resultState) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700623 if (mIsSummaryWithChildren) {
Selim Cinekbbcebde2016-11-09 18:28:20 -0800624 ExpandableViewState parentState = resultState.getViewStateForView(this);
Selim Cinekb5605e52015-02-20 18:21:41 +0100625 mChildrenContainer.getState(resultState, parentState);
626 }
627 }
628
629 public void applyChildrenState(StackScrollState state) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700630 if (mIsSummaryWithChildren) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100631 mChildrenContainer.applyState(state);
632 }
633 }
634
635 public void prepareExpansionChanged(StackScrollState state) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700636 if (mIsSummaryWithChildren) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100637 mChildrenContainer.prepareExpansionChanged(state);
638 }
639 }
640
Selim Cinek0cfbef42016-11-09 19:06:36 -0800641 public void startChildAnimation(StackScrollState finalState, AnimationProperties properties) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700642 if (mIsSummaryWithChildren) {
Selim Cinek0cfbef42016-11-09 19:06:36 -0800643 mChildrenContainer.startAnimationToState(finalState, properties);
Selim Cinekb5605e52015-02-20 18:21:41 +0100644 }
645 }
646
647 public ExpandableNotificationRow getViewAtPosition(float y) {
Selim Cinek43d30f02016-03-04 10:51:32 -0800648 if (!mIsSummaryWithChildren || !mChildrenExpanded) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100649 return this;
650 } else {
651 ExpandableNotificationRow view = mChildrenContainer.getViewAtPosition(y);
652 return view == null ? this : view;
653 }
654 }
655
Selim Cinekab29aeb2015-02-20 18:18:32 +0100656 public NotificationGuts getGuts() {
657 return mGuts;
658 }
659
Selim Cinek684a4422015-04-15 16:18:39 -0700660 /**
661 * Set this notification to be pinned to the top if {@link #isHeadsUp()} is true. By doing this
662 * the notification will be rendered on top of the screen.
663 *
664 * @param pinned whether it is pinned
665 */
666 public void setPinned(boolean pinned) {
Selim Cinekdef35a82016-05-03 15:52:51 -0700667 int intrinsicHeight = getIntrinsicHeight();
Selim Cinek5cf1d052017-06-01 17:36:46 -0700668 boolean wasAboveShelf = isAboveShelf();
Selim Cinek684a4422015-04-15 16:18:39 -0700669 mIsPinned = pinned;
Selim Cinekdef35a82016-05-03 15:52:51 -0700670 if (intrinsicHeight != getIntrinsicHeight()) {
Selim Cinekbb42b7d2016-08-10 13:02:38 -0700671 notifyHeightChanged(false /* needsAnimation */);
Selim Cinekdef35a82016-05-03 15:52:51 -0700672 }
Selim Cinek31aada42015-12-18 17:51:15 -0800673 if (pinned) {
674 setIconAnimationRunning(true);
675 mExpandedWhenPinned = false;
676 } else if (mExpandedWhenPinned) {
677 setUserExpanded(true);
678 }
Selim Cinek98713a42015-09-21 15:47:20 +0200679 setChronometerRunning(mLastChronometerRunning);
Selim Cinek5cf1d052017-06-01 17:36:46 -0700680 if (isAboveShelf() != wasAboveShelf) {
681 mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
682 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700683 }
684
Selim Cinek684a4422015-04-15 16:18:39 -0700685 public boolean isPinned() {
686 return mIsPinned;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700687 }
688
Selim Cinekd127d792016-11-01 19:11:41 -0700689 @Override
690 public int getPinnedHeadsUpHeight() {
691 return getPinnedHeadsUpHeight(true /* atLeastMinHeight */);
692 }
693
Selim Cinek31aada42015-12-18 17:51:15 -0800694 /**
695 * @param atLeastMinHeight should the value returned be at least the minimum height.
696 * Used to avoid cyclic calls
697 * @return the height of the heads up notification when pinned
698 */
Selim Cinekd127d792016-11-01 19:11:41 -0700699 private int getPinnedHeadsUpHeight(boolean atLeastMinHeight) {
Selim Cinek77019c72015-12-09 10:18:02 -0800700 if (mIsSummaryWithChildren) {
701 return mChildrenContainer.getIntrinsicHeight();
702 }
Selim Cinek31aada42015-12-18 17:51:15 -0800703 if(mExpandedWhenPinned) {
704 return Math.max(getMaxExpandHeight(), mHeadsUpHeight);
705 } else if (atLeastMinHeight) {
Selim Cinek567e8452016-03-24 10:54:56 -0700706 return Math.max(getCollapsedHeight(), mHeadsUpHeight);
Selim Cinek31aada42015-12-18 17:51:15 -0800707 } else {
708 return mHeadsUpHeight;
709 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700710 }
711
Jorim Jaggi5eb67c22015-08-19 19:50:49 -0700712 /**
713 * Mark whether this notification was just clicked, i.e. the user has just clicked this
714 * notification in this frame.
715 */
716 public void setJustClicked(boolean justClicked) {
717 mJustClicked = justClicked;
718 }
719
720 /**
721 * @return true if this notification has been clicked in this frame, false otherwise
722 */
723 public boolean wasJustClicked() {
724 return mJustClicked;
725 }
726
Selim Cinek98713a42015-09-21 15:47:20 +0200727 public void setChronometerRunning(boolean running) {
728 mLastChronometerRunning = running;
729 setChronometerRunning(running, mPrivateLayout);
730 setChronometerRunning(running, mPublicLayout);
731 if (mChildrenContainer != null) {
732 List<ExpandableNotificationRow> notificationChildren =
733 mChildrenContainer.getNotificationChildren();
734 for (int i = 0; i < notificationChildren.size(); i++) {
735 ExpandableNotificationRow child = notificationChildren.get(i);
736 child.setChronometerRunning(running);
737 }
738 }
739 }
740
741 private void setChronometerRunning(boolean running, NotificationContentView layout) {
742 if (layout != null) {
743 running = running || isPinned();
744 View contractedChild = layout.getContractedChild();
745 View expandedChild = layout.getExpandedChild();
746 View headsUpChild = layout.getHeadsUpChild();
747 setChronometerRunningForChild(running, contractedChild);
748 setChronometerRunningForChild(running, expandedChild);
749 setChronometerRunningForChild(running, headsUpChild);
750 }
751 }
752
753 private void setChronometerRunningForChild(boolean running, View child) {
754 if (child != null) {
755 View chronometer = child.findViewById(com.android.internal.R.id.chronometer);
756 if (chronometer instanceof Chronometer) {
757 ((Chronometer) chronometer).setStarted(running);
758 }
759 }
760 }
761
Selim Cinekea4bef72015-12-02 15:51:10 -0800762 public NotificationHeaderView getNotificationHeader() {
Mady Mellorb0a82462016-04-30 17:31:02 -0700763 if (mIsSummaryWithChildren) {
764 return mChildrenContainer.getHeaderView();
Selim Cinek8d6440d2015-10-22 13:00:05 -0700765 }
Selim Cinekea4bef72015-12-02 15:51:10 -0800766 return mPrivateLayout.getNotificationHeader();
Selim Cinek8d6440d2015-10-22 13:00:05 -0700767 }
768
Selim Cinek414ad332017-02-24 19:06:12 -0800769 /**
770 * @return the currently visible notification header. This can be different from
771 * {@link #getNotificationHeader()} in case it is a low-priority group.
772 */
773 public NotificationHeaderView getVisibleNotificationHeader() {
Selim Cinekaa3901a2016-08-05 11:04:37 -0700774 if (mIsSummaryWithChildren && !mShowingPublic) {
Selim Cinek414ad332017-02-24 19:06:12 -0800775 return mChildrenContainer.getVisibleHeader();
Selim Cinek34eda5e2016-02-18 17:10:43 -0800776 }
777 return getShowingLayout().getVisibleNotificationHeader();
778 }
779
Selim Cinekb26afa32017-06-29 10:28:17 +0200780
781 /**
782 * @return the contracted notification header. This can be different from
783 * {@link #getNotificationHeader()} and also {@link #getVisibleNotificationHeader()} and only
784 * returns the contracted version.
785 */
786 public NotificationHeaderView getContractedNotificationHeader() {
787 if (mIsSummaryWithChildren) {
788 return mChildrenContainer.getHeaderView();
789 }
790 return mPrivateLayout.getContractedNotificationHeader();
791 }
792
Selim Cinek570981d2015-12-01 11:37:01 -0800793 public void setOnExpandClickListener(OnExpandClickListener onExpandClickListener) {
794 mOnExpandClickListener = onExpandClickListener;
795 }
796
Geoffrey Pitsch409db272017-08-28 14:51:52 +0000797 public void setLongPressListener(LongPressListener longPressListener) {
798 mLongPressListener = longPressListener;
799 }
800
Selim Cinekddf1b392016-05-27 16:33:10 -0700801 @Override
802 public void setOnClickListener(@Nullable OnClickListener l) {
803 super.setOnClickListener(l);
804 mOnClickListener = l;
805 updateClickAndFocus();
806 }
807
808 private void updateClickAndFocus() {
809 boolean normalChild = !isChildInGroup() || isGroupExpanded();
810 boolean clickable = mOnClickListener != null && normalChild;
811 if (isFocusable() != normalChild) {
812 setFocusable(normalChild);
813 }
814 if (isClickable() != clickable) {
815 setClickable(clickable);
816 }
817 }
818
Selim Cinek31aada42015-12-18 17:51:15 -0800819 public void setHeadsUpManager(HeadsUpManager headsUpManager) {
820 mHeadsUpManager = headsUpManager;
821 }
822
Mady Mellor87d79452017-01-10 11:52:52 -0800823 public void setGutsView(MenuItem item) {
Mady Mellor95d743c2017-01-10 12:05:27 -0800824 if (mGuts != null && item.getGutsView() instanceof GutsContent) {
825 ((GutsContent) item.getGutsView()).setGutsParent(mGuts);
826 mGuts.setGutsContent((GutsContent) item.getGutsView());
Mady Mellor87d79452017-01-10 11:52:52 -0800827 }
828 }
829
Mady Mellor95d743c2017-01-10 12:05:27 -0800830 @Override
831 protected void onAttachedToWindow() {
832 super.onAttachedToWindow();
833 Dependency.get(PluginManager.class).addPluginListener(this,
834 NotificationMenuRowPlugin.class, false /* Allow multiple */);
835 }
836
837 @Override
838 protected void onDetachedFromWindow() {
839 super.onDetachedFromWindow();
840 Dependency.get(PluginManager.class).removePluginListener(this);
841 }
842
843 @Override
844 public void onPluginConnected(NotificationMenuRowPlugin plugin, Context pluginContext) {
845 boolean existed = mMenuRow.getMenuView() != null;
846 if (existed) {
847 removeView(mMenuRow.getMenuView());
848 }
849 mMenuRow = plugin;
850 if (mMenuRow.useDefaultMenuItems()) {
Mady Mellor4c197602017-04-10 17:57:52 -0700851 ArrayList<MenuItem> items = new ArrayList<>();
852 items.add(NotificationMenuRow.createInfoItem(mContext));
853 items.add(NotificationMenuRow.createSnoozeItem(mContext));
854 mMenuRow.setMenuItems(items);
Mady Mellor95d743c2017-01-10 12:05:27 -0800855 }
856 if (existed) {
857 createMenu();
858 }
859 }
860
861 @Override
862 public void onPluginDisconnected(NotificationMenuRowPlugin plugin) {
863 boolean existed = mMenuRow.getMenuView() != null;
864 mMenuRow = new NotificationMenuRow(mContext); // Back to default
865 if (existed) {
866 createMenu();
867 }
868 }
869
870 public NotificationMenuRowPlugin createMenu() {
871 if (mMenuRow.getMenuView() == null) {
Mady Mellor4ab28202017-06-06 11:42:50 -0700872 mMenuRow.createMenu(this, mStatusBarNotification);
Mady Mellor95d743c2017-01-10 12:05:27 -0800873 mMenuRow.setAppName(mAppName);
874 FrameLayout.LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT,
875 LayoutParams.MATCH_PARENT);
876 addView(mMenuRow.getMenuView(), MENU_VIEW_INDEX, lp);
877 }
878 return mMenuRow;
879 }
880
Mady Mellor95d743c2017-01-10 12:05:27 -0800881 public NotificationMenuRowPlugin getProvider() {
882 return mMenuRow;
883 }
884
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700885 @Override
Selim Cinek1a48bab2017-02-17 19:38:40 -0800886 public void onDensityOrFontScaleChanged() {
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700887 super.onDensityOrFontScaleChanged();
Selim Cinek01af3342016-02-09 19:25:31 -0800888 initDimens();
Anthony Chenad4d1582017-04-10 16:07:58 -0700889 initBackground();
Selim Cinek817abe72017-05-24 11:08:55 -0700890 // Let's update our childrencontainer. This is intentionally not guarded with
891 // mIsSummaryWithChildren since we might have had children but not anymore.
892 if (mChildrenContainer != null) {
893 mChildrenContainer.reInflateViews(mExpandClickListener, mEntry.notification);
Selim Cinek01af3342016-02-09 19:25:31 -0800894 }
895 if (mGuts != null) {
896 View oldGuts = mGuts;
897 int index = indexOfChild(oldGuts);
898 removeView(oldGuts);
899 mGuts = (NotificationGuts) LayoutInflater.from(mContext).inflate(
900 R.layout.notification_guts, this, false);
901 mGuts.setVisibility(oldGuts.getVisibility());
902 addView(mGuts, index);
903 }
Mady Mellor95d743c2017-01-10 12:05:27 -0800904 View oldMenu = mMenuRow.getMenuView();
905 if (oldMenu != null) {
Mady Mellor761cde12017-01-10 11:36:39 -0800906 int menuIndex = indexOfChild(oldMenu);
907 removeView(oldMenu);
Mady Mellor4ab28202017-06-06 11:42:50 -0700908 mMenuRow.createMenu(ExpandableNotificationRow.this, mStatusBarNotification);
Mady Mellor761cde12017-01-10 11:36:39 -0800909 mMenuRow.setAppName(mAppName);
Mady Mellor95d743c2017-01-10 12:05:27 -0800910 addView(mMenuRow.getMenuView(), menuIndex);
Mady Mellor4b80b102016-01-22 08:03:58 -0800911 }
Adrian Rooseb434ff2017-01-11 11:18:48 -0800912 for (NotificationContentView l : mLayouts) {
913 l.reInflateViews();
914 }
Selim Cinek1a48bab2017-02-17 19:38:40 -0800915 mNotificationInflater.onDensityOrFontScaleChanged();
916 onNotificationUpdated();
Selim Cinek01af3342016-02-09 19:25:31 -0800917 }
918
Mady Mellor9d03a522017-04-04 18:45:30 -0700919 @Override
920 public void onConfigurationChanged(Configuration newConfig) {
921 if (mMenuRow.getMenuView() != null) {
922 mMenuRow.onConfigurationChanged();
923 }
924 }
925
Selim Cinekc3179332016-03-04 14:44:56 -0800926 public void setContentBackground(int customBackgroundColor, boolean animate,
927 NotificationContentView notificationContentView) {
928 if (getShowingLayout() == notificationContentView) {
929 setTintColor(customBackgroundColor, animate);
930 }
931 }
932
Adrian Roos0bd8a4b2016-03-14 16:21:44 -0700933 public void closeRemoteInput() {
Adrian Rooseb434ff2017-01-11 11:18:48 -0800934 for (NotificationContentView l : mLayouts) {
935 l.closeRemoteInput();
936 }
Adrian Roos0bd8a4b2016-03-14 16:21:44 -0700937 }
938
Selim Cinekc897bd32016-03-18 17:32:31 -0700939 /**
940 * Set by how much the single line view should be indented.
941 */
942 public void setSingleLineWidthIndention(int indention) {
943 mPrivateLayout.setSingleLineWidthIndention(indention);
944 }
945
946 public int getNotificationColor() {
Selim Cinek4bb59342016-04-08 19:29:35 -0700947 return mNotificationColor;
948 }
949
950 private void updateNotificationColor() {
951 mNotificationColor = NotificationColorUtil.resolveContrastColor(mContext,
Selim Cinekac5f0272017-05-02 16:05:41 -0700952 getStatusBarNotification().getNotification().color,
953 getBackgroundColorWithoutTint());
Adrian Roos6f6e1592017-05-02 16:22:53 -0700954 mNotificationColorAmbient = NotificationColorUtil.resolveAmbientColor(mContext,
955 getStatusBarNotification().getNotification().color);
Selim Cinekc897bd32016-03-18 17:32:31 -0700956 }
957
958 public HybridNotificationView getSingleLineView() {
959 return mPrivateLayout.getSingleLineView();
960 }
961
Adrian Roos6f6e1592017-05-02 16:22:53 -0700962 public HybridNotificationView getAmbientSingleLineView() {
963 return getShowingLayout().getAmbientSingleLineChild();
964 }
965
Selim Cinekf07d0622016-03-21 19:52:52 -0700966 public boolean isOnKeyguard() {
967 return mOnKeyguard;
968 }
969
Selim Cinekc1e389d2016-04-07 11:02:57 -0700970 public void removeAllChildren() {
971 List<ExpandableNotificationRow> notificationChildren
972 = mChildrenContainer.getNotificationChildren();
973 ArrayList<ExpandableNotificationRow> clonedList = new ArrayList<>(notificationChildren);
974 for (int i = 0; i < clonedList.size(); i++) {
975 ExpandableNotificationRow row = clonedList.get(i);
Selim Cinek3f19f602016-05-02 18:01:56 -0700976 if (row.keepInParent()) {
977 continue;
978 }
Selim Cinekc1e389d2016-04-07 11:02:57 -0700979 mChildrenContainer.removeNotification(row);
Selim Cinekc1e389d2016-04-07 11:02:57 -0700980 row.setIsChildInGroup(false, null);
981 }
982 onChildrenCountChanged();
983 }
984
Selim Cinek1b2a05e2016-04-28 14:20:39 -0700985 public void setForceUnlocked(boolean forceUnlocked) {
986 mForceUnlocked = forceUnlocked;
987 if (mIsSummaryWithChildren) {
988 List<ExpandableNotificationRow> notificationChildren = getNotificationChildren();
989 for (ExpandableNotificationRow child : notificationChildren) {
990 child.setForceUnlocked(forceUnlocked);
991 }
992 }
993 }
994
Selim Cineke9bad242016-06-15 11:46:37 -0700995 public void setDismissed(boolean dismissed, boolean fromAccessibility) {
Selim Cinek3f19f602016-05-02 18:01:56 -0700996 mDismissed = dismissed;
Selim Cineke9bad242016-06-15 11:46:37 -0700997 mGroupParentWhenDismissed = mNotificationParent;
998 mRefocusOnDismiss = fromAccessibility;
999 mChildAfterViewWhenDismissed = null;
1000 if (isChildInGroup()) {
1001 List<ExpandableNotificationRow> notificationChildren =
1002 mNotificationParent.getNotificationChildren();
1003 int i = notificationChildren.indexOf(this);
1004 if (i != -1 && i < notificationChildren.size() - 1) {
1005 mChildAfterViewWhenDismissed = notificationChildren.get(i + 1);
1006 }
1007 }
Selim Cinek3f19f602016-05-02 18:01:56 -07001008 }
1009
1010 public boolean isDismissed() {
1011 return mDismissed;
1012 }
1013
1014 public boolean keepInParent() {
1015 return mKeepInParent;
1016 }
1017
1018 public void setKeepInParent(boolean keepInParent) {
1019 mKeepInParent = keepInParent;
1020 }
1021
1022 public boolean isRemoved() {
1023 return mRemoved;
1024 }
1025
Adrian Roosd009ab12016-05-20 17:58:53 -07001026 public void setRemoved() {
1027 mRemoved = true;
Selim Cinekef8c2252017-02-10 14:52:18 -08001028 mTranslationWhenRemoved = getTranslationY();
1029 mWasChildInGroupWhenRemoved = isChildInGroup();
1030 if (isChildInGroup()) {
1031 mTranslationWhenRemoved += getNotificationParent().getTranslationY();
1032 }
Adrian Roosd009ab12016-05-20 17:58:53 -07001033 mPrivateLayout.setRemoved();
Selim Cinek3f19f602016-05-02 18:01:56 -07001034 }
1035
Selim Cinekef8c2252017-02-10 14:52:18 -08001036 public boolean wasChildInGroupWhenRemoved() {
1037 return mWasChildInGroupWhenRemoved;
1038 }
1039
1040 public float getTranslationWhenRemoved() {
1041 return mTranslationWhenRemoved;
1042 }
1043
Selim Cinekd1395642016-04-28 12:22:42 -07001044 public NotificationChildrenContainer getChildrenContainer() {
1045 return mChildrenContainer;
1046 }
1047
Selim Cinekcafa87f2016-10-26 17:00:17 -07001048 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
Selim Cinek5cf1d052017-06-01 17:36:46 -07001049 boolean wasAboveShelf = isAboveShelf();
Selim Cinekcafa87f2016-10-26 17:00:17 -07001050 mHeadsupDisappearRunning = headsUpAnimatingAway;
1051 mPrivateLayout.setHeadsUpAnimatingAway(headsUpAnimatingAway);
Selim Cinek5cf1d052017-06-01 17:36:46 -07001052 if (isAboveShelf() != wasAboveShelf) {
1053 mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
1054 }
Selim Cinekcafa87f2016-10-26 17:00:17 -07001055 }
1056
1057 /**
1058 * @return if the view was just heads upped and is now animating away. During such a time the
1059 * layout needs to be kept consistent
1060 */
1061 public boolean isHeadsUpAnimatingAway() {
1062 return mHeadsupDisappearRunning;
Selim Cinek73cf02a2016-06-17 13:08:00 -07001063 }
1064
Selim Cineke9bad242016-06-15 11:46:37 -07001065 public View getChildAfterViewWhenDismissed() {
1066 return mChildAfterViewWhenDismissed;
1067 }
1068
1069 public View getGroupParentWhenDismissed() {
1070 return mGroupParentWhenDismissed;
1071 }
1072
Selim Cinek9e624e72016-07-20 13:46:49 -07001073 public void performDismiss() {
Selim Cineke9079112016-12-14 14:41:01 -08001074 if (mOnDismissRunnable != null) {
1075 mOnDismissRunnable.run();
1076 }
Selim Cinek9e624e72016-07-20 13:46:49 -07001077 }
1078
Selim Cineke9079112016-12-14 14:41:01 -08001079 public void setOnDismissRunnable(Runnable onDismissRunnable) {
1080 mOnDismissRunnable = onDismissRunnable;
Selim Cinek9e624e72016-07-20 13:46:49 -07001081 }
1082
Selim Cinek281c2022016-10-13 19:14:43 -07001083 public View getNotificationIcon() {
Selim Cinek414ad332017-02-24 19:06:12 -08001084 NotificationHeaderView notificationHeader = getVisibleNotificationHeader();
Selim Cinek281c2022016-10-13 19:14:43 -07001085 if (notificationHeader != null) {
1086 return notificationHeader.getIcon();
1087 }
1088 return null;
1089 }
1090
1091 /**
1092 * @return whether the notification is currently showing a view with an icon.
1093 */
1094 public boolean isShowingIcon() {
Mady Mellor434180c2017-02-13 11:29:42 -08001095 if (areGutsExposed()) {
1096 return false;
1097 }
Selim Cinek2ffa02f2017-03-06 15:56:37 -08001098 return getVisibleNotificationHeader() != null;
Selim Cinek281c2022016-10-13 19:14:43 -07001099 }
1100
Selim Cinek0242fbb2016-10-19 13:38:32 -07001101 /**
1102 * Set how much this notification is transformed into an icon.
1103 *
Selim Cinek2b549f42016-11-22 16:38:51 -08001104 * @param contentTransformationAmount A value from 0 to 1 indicating how much we are transformed
1105 * to the content away
Selim Cinek875a3a12016-11-18 17:52:16 -08001106 * @param isLastChild is this the last child in the list. If true, then the transformation is
1107 * different since it's content fades out.
Selim Cinek0242fbb2016-10-19 13:38:32 -07001108 */
Selim Cinek2b549f42016-11-22 16:38:51 -08001109 public void setContentTransformationAmount(float contentTransformationAmount,
1110 boolean isLastChild) {
Selim Cinek875a3a12016-11-18 17:52:16 -08001111 boolean changeTransformation = isLastChild != mIsLastChild;
Selim Cinek2b549f42016-11-22 16:38:51 -08001112 changeTransformation |= mContentTransformationAmount != contentTransformationAmount;
Selim Cinek875a3a12016-11-18 17:52:16 -08001113 mIsLastChild = isLastChild;
Selim Cinek2b549f42016-11-22 16:38:51 -08001114 mContentTransformationAmount = contentTransformationAmount;
Selim Cinek875a3a12016-11-18 17:52:16 -08001115 if (changeTransformation) {
1116 updateContentTransformation();
Selim Cinek2b549f42016-11-22 16:38:51 -08001117 }
1118 }
1119
1120 /**
1121 * Set the icons to be visible of this notification.
1122 */
1123 public void setIconsVisible(boolean iconsVisible) {
1124 if (iconsVisible != mIconsVisible) {
1125 mIconsVisible = iconsVisible;
1126 updateIconVisibilities();
Selim Cinek0242fbb2016-10-19 13:38:32 -07001127 }
1128 }
1129
Selim Cinekdb167372016-11-17 15:41:17 -08001130 @Override
1131 protected void onBelowSpeedBumpChanged() {
1132 updateIconVisibilities();
1133 }
1134
Selim Cinek875a3a12016-11-18 17:52:16 -08001135 private void updateContentTransformation() {
1136 float contentAlpha;
Selim Cinek2b549f42016-11-22 16:38:51 -08001137 float translationY = -mContentTransformationAmount * mIconTransformContentShift;
Selim Cinek875a3a12016-11-18 17:52:16 -08001138 if (mIsLastChild) {
Selim Cinek2b549f42016-11-22 16:38:51 -08001139 contentAlpha = 1.0f - mContentTransformationAmount;
Selim Cinek875a3a12016-11-18 17:52:16 -08001140 contentAlpha = Math.min(contentAlpha / 0.5f, 1.0f);
Selim Cinek0242fbb2016-10-19 13:38:32 -07001141 contentAlpha = Interpolators.ALPHA_OUT.getInterpolation(contentAlpha);
Selim Cinek875a3a12016-11-18 17:52:16 -08001142 translationY *= 0.4f;
1143 } else {
1144 contentAlpha = 1.0f;
1145 }
Adrian Rooseb434ff2017-01-11 11:18:48 -08001146 for (NotificationContentView l : mLayouts) {
1147 l.setAlpha(contentAlpha);
1148 l.setTranslationY(translationY);
1149 }
Selim Cinek875a3a12016-11-18 17:52:16 -08001150 if (mChildrenContainer != null) {
1151 mChildrenContainer.setAlpha(contentAlpha);
1152 mChildrenContainer.setTranslationY(translationY);
1153 // TODO: handle children fade out better
Selim Cinek0242fbb2016-10-19 13:38:32 -07001154 }
1155 }
1156
1157 private void updateIconVisibilities() {
Selim Cinek17e1b692016-12-02 18:19:11 -08001158 boolean visible = isChildInGroup()
1159 || (isBelowSpeedBump() && !NotificationShelf.SHOW_AMBIENT_ICONS)
1160 || mIconsVisible;
Adrian Rooseb434ff2017-01-11 11:18:48 -08001161 for (NotificationContentView l : mLayouts) {
1162 l.setIconsVisible(visible);
1163 }
Selim Cinekdb167372016-11-17 15:41:17 -08001164 if (mChildrenContainer != null) {
1165 mChildrenContainer.setIconsVisible(visible);
Selim Cinek0242fbb2016-10-19 13:38:32 -07001166 }
1167 }
1168
Selim Cinek875a3a12016-11-18 17:52:16 -08001169 /**
1170 * Get the relative top padding of a view relative to this view. This recursively walks up the
1171 * hierarchy and does the corresponding measuring.
1172 *
1173 * @param view the view to the the padding for. The requested view has to be a child of this
1174 * notification.
1175 * @return the toppadding
1176 */
1177 public int getRelativeTopPadding(View view) {
1178 int topPadding = 0;
1179 while (view.getParent() instanceof ViewGroup) {
1180 topPadding += view.getTop();
1181 view = (View) view.getParent();
1182 if (view instanceof ExpandableNotificationRow) {
1183 return topPadding;
1184 }
1185 }
1186 return topPadding;
1187 }
1188
Selim Cineka1d97902016-12-14 16:31:40 -08001189 public float getContentTranslation() {
1190 return mPrivateLayout.getTranslationY();
1191 }
1192
Selim Cinek6743c0b2017-01-18 18:24:01 -08001193 public void setIsLowPriority(boolean isLowPriority) {
1194 mIsLowPriority = isLowPriority;
1195 mPrivateLayout.setIsLowPriority(isLowPriority);
Selim Cinek1a48bab2017-02-17 19:38:40 -08001196 mNotificationInflater.setIsLowPriority(mIsLowPriority);
Selim Cinek6743c0b2017-01-18 18:24:01 -08001197 if (mChildrenContainer != null) {
1198 mChildrenContainer.setIsLowPriority(isLowPriority);
1199 }
1200 }
1201
Selim Cinek55a3e732017-05-25 18:30:10 -07001202
1203 public void setLowPriorityStateUpdated(boolean lowPriorityStateUpdated) {
1204 mLowPriorityStateUpdated = lowPriorityStateUpdated;
1205 }
1206
1207 public boolean hasLowPriorityStateUpdated() {
1208 return mLowPriorityStateUpdated;
1209 }
1210
Selim Cinek414ad332017-02-24 19:06:12 -08001211 public boolean isLowPriority() {
1212 return mIsLowPriority;
1213 }
1214
Selim Cinek7d1009b2017-01-25 15:28:28 -08001215 public void setUseIncreasedCollapsedHeight(boolean use) {
1216 mUseIncreasedCollapsedHeight = use;
Selim Cinek1a48bab2017-02-17 19:38:40 -08001217 mNotificationInflater.setUsesIncreasedHeight(use);
Selim Cinek7d1009b2017-01-25 15:28:28 -08001218 }
1219
Selim Cinek87ed69b2017-02-09 15:59:43 -08001220 public void setUseIncreasedHeadsUpHeight(boolean use) {
1221 mUseIncreasedHeadsUpHeight = use;
Selim Cinek1a48bab2017-02-17 19:38:40 -08001222 mNotificationInflater.setUsesIncreasedHeadsUpHeight(use);
1223 }
1224
1225 public void setRemoteViewClickHandler(RemoteViews.OnClickHandler remoteViewClickHandler) {
1226 mNotificationInflater.setRemoteViewClickHandler(remoteViewClickHandler);
Selim Cinek87ed69b2017-02-09 15:59:43 -08001227 }
1228
Selim Cinek5ba22542017-04-20 15:16:10 -07001229 public void setInflationCallback(InflationCallback callback) {
1230 mNotificationInflater.setInflationCallback(callback);
Selim Cinekc478f902017-02-22 20:55:44 -08001231 }
1232
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07001233 public void setNeedsRedaction(boolean needsRedaction) {
1234 mNotificationInflater.setRedactAmbient(needsRedaction);
1235 }
1236
Selim Cinek5ba22542017-04-20 15:16:10 -07001237 @VisibleForTesting
1238 public NotificationInflater getNotificationInflater() {
1239 return mNotificationInflater;
1240 }
1241
Adrian Roos6f6e1592017-05-02 16:22:53 -07001242 public int getNotificationColorAmbient() {
1243 return mNotificationColorAmbient;
1244 }
1245
Chris Wren78403d72014-07-28 10:23:24 +01001246 public interface ExpansionLogger {
Anthony Chen6bf88a02017-04-10 14:41:44 -07001247 void logNotificationExpansion(String key, boolean userAction, boolean expanded);
Chris Wren78403d72014-07-28 10:23:24 +01001248 }
Selim Cinek1685e632014-04-08 02:27:49 +02001249
Chris Wren51c75102013-07-16 20:49:17 -04001250 public ExpandableNotificationRow(Context context, AttributeSet attrs) {
1251 super(context, attrs);
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -07001252 mFalsingManager = FalsingManager.getInstance(context);
Selim Cinek1a48bab2017-02-17 19:38:40 -08001253 mNotificationInflater = new NotificationInflater(this);
Mady Mellor95d743c2017-01-10 12:05:27 -08001254 mMenuRow = new NotificationMenuRow(mContext);
Selim Cinek01af3342016-02-09 19:25:31 -08001255 initDimens();
1256 }
1257
1258 private void initDimens() {
Selim Cinekf619ffc2016-02-17 14:53:05 -08001259 mNotificationMinHeightLegacy = getFontScaledHeight(R.dimen.notification_min_height_legacy);
1260 mNotificationMinHeight = getFontScaledHeight(R.dimen.notification_min_height);
Selim Cinek7d1009b2017-01-25 15:28:28 -08001261 mNotificationMinHeightLarge = getFontScaledHeight(
Selim Cinek87ed69b2017-02-09 15:59:43 -08001262 R.dimen.notification_min_height_increased);
Selim Cinekf619ffc2016-02-17 14:53:05 -08001263 mNotificationMaxHeight = getFontScaledHeight(R.dimen.notification_max_height);
Adrian Roos0aac04f2016-12-08 15:59:29 -08001264 mNotificationAmbientHeight = getFontScaledHeight(R.dimen.notification_ambient_height);
Selim Cinekf619ffc2016-02-17 14:53:05 -08001265 mMaxHeadsUpHeightLegacy = getFontScaledHeight(
Selim Cinek77019c72015-12-09 10:18:02 -08001266 R.dimen.notification_max_heads_up_height_legacy);
Selim Cinekf619ffc2016-02-17 14:53:05 -08001267 mMaxHeadsUpHeight = getFontScaledHeight(R.dimen.notification_max_heads_up_height);
Selim Cinek87ed69b2017-02-09 15:59:43 -08001268 mMaxHeadsUpHeightIncreased = getFontScaledHeight(
1269 R.dimen.notification_max_heads_up_height_increased);
Anthony Chen6bf88a02017-04-10 14:41:44 -07001270
Anthony Chen7acbb772017-04-07 16:45:25 -07001271 Resources res = getResources();
1272 mIncreasedPaddingBetweenElements = res.getDimensionPixelSize(
1273 R.dimen.notification_divider_height_increased);
1274 mIconTransformContentShiftNoIcon = res.getDimensionPixelSize(
Selim Cinek0242fbb2016-10-19 13:38:32 -07001275 R.dimen.notification_icon_transform_content_shift);
Anthony Chen6bf88a02017-04-10 14:41:44 -07001276 mEnableNonGroupedNotificationExpand =
1277 res.getBoolean(R.bool.config_enableNonGroupedNotificationExpand);
1278 mShowGroupBackgroundWhenExpanded =
1279 res.getBoolean(R.bool.config_showGroupNotificationBgWhenExpanded);
Selim Cinekf619ffc2016-02-17 14:53:05 -08001280 }
1281
1282 /**
1283 * @param dimenId the dimen to look up
1284 * @return the font scaled dimen as if it were in sp but doesn't shrink sizes below dp
1285 */
1286 private int getFontScaledHeight(int dimenId) {
1287 int dimensionPixelSize = getResources().getDimensionPixelSize(dimenId);
1288 float factor = Math.max(1.0f, getResources().getDisplayMetrics().scaledDensity /
1289 getResources().getDisplayMetrics().density);
1290 return (int) (dimensionPixelSize * factor);
Chris Wren51c75102013-07-16 20:49:17 -04001291 }
1292
Christoph Studera7fe6312014-06-27 19:32:44 +02001293 /**
1294 * Resets this view so it can be re-used for an updated notification.
1295 */
1296 public void reset() {
Jorim Jaggiae441282014-08-01 02:45:18 +02001297 mShowingPublicInitialized = false;
Selim Cinek31094df2014-08-14 19:28:15 +02001298 onHeightReset();
Selim Cinek6e28a672014-09-05 14:43:28 +02001299 requestLayout();
Christoph Studera7fe6312014-06-27 19:32:44 +02001300 }
1301
Jorim Jaggi251957d2014-04-09 04:24:09 +02001302 @Override
1303 protected void onFinishInflate() {
1304 super.onFinishInflate();
Jorim Jaggibe565df2014-04-28 17:51:23 +02001305 mPublicLayout = (NotificationContentView) findViewById(R.id.expandedPublic);
1306 mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded);
Adrian Rooseb434ff2017-01-11 11:18:48 -08001307 mLayouts = new NotificationContentView[] {mPrivateLayout, mPublicLayout};
1308
1309 for (NotificationContentView l : mLayouts) {
1310 l.setExpandClickListener(mExpandClickListener);
1311 l.setContainingNotification(this);
1312 }
Selim Cinekab29aeb2015-02-20 18:18:32 +01001313 mGutsStub = (ViewStub) findViewById(R.id.notification_guts_stub);
1314 mGutsStub.setOnInflateListener(new ViewStub.OnInflateListener() {
Jorim Jaggib1cd3c12014-09-08 19:55:17 +02001315 @Override
1316 public void onInflate(ViewStub stub, View inflated) {
1317 mGuts = (NotificationGuts) inflated;
1318 mGuts.setClipTopAmount(getClipTopAmount());
1319 mGuts.setActualHeight(getActualHeight());
Selim Cinekab29aeb2015-02-20 18:18:32 +01001320 mGutsStub = null;
Jorim Jaggib1cd3c12014-09-08 19:55:17 +02001321 }
1322 });
Selim Cinekb5605e52015-02-20 18:21:41 +01001323 mChildrenContainerStub = (ViewStub) findViewById(R.id.child_container_stub);
1324 mChildrenContainerStub.setOnInflateListener(new ViewStub.OnInflateListener() {
1325
1326 @Override
1327 public void onInflate(ViewStub stub, View inflated) {
1328 mChildrenContainer = (NotificationChildrenContainer) inflated;
Selim Cinek6743c0b2017-01-18 18:24:01 -08001329 mChildrenContainer.setIsLowPriority(mIsLowPriority);
Selim Cinek414ad332017-02-24 19:06:12 -08001330 mChildrenContainer.setContainingNotification(ExpandableNotificationRow.this);
Selim Cinekc897bd32016-03-18 17:32:31 -07001331 mChildrenContainer.onNotificationUpdated();
Anthony Chen7acbb772017-04-07 16:45:25 -07001332
1333 if (mShouldTranslateContents) {
1334 mTranslateableViews.add(mChildrenContainer);
1335 }
Selim Cinekb5605e52015-02-20 18:21:41 +01001336 }
1337 });
Mady Mellor4b80b102016-01-22 08:03:58 -08001338
Anthony Chen7acbb772017-04-07 16:45:25 -07001339 if (mShouldTranslateContents) {
1340 // Add the views that we translate to reveal the menu
1341 mTranslateableViews = new ArrayList<>();
1342 for (int i = 0; i < getChildCount(); i++) {
1343 mTranslateableViews.add(getChildAt(i));
1344 }
1345 // Remove views that don't translate
1346 mTranslateableViews.remove(mChildrenContainerStub);
1347 mTranslateableViews.remove(mGutsStub);
Mady Mellor4b80b102016-01-22 08:03:58 -08001348 }
Mady Mellor4b80b102016-01-22 08:03:58 -08001349 }
1350
Geoffrey Pitsch409db272017-08-28 14:51:52 +00001351 private void doLongClickCallback() {
1352 doLongClickCallback(getWidth() / 2, getHeight() / 2);
1353 }
1354
1355 public void doLongClickCallback(int x, int y) {
1356 createMenu();
1357 MenuItem menuItem = getProvider().getLongpressMenuItem(mContext);
1358 if (mLongPressListener != null && menuItem != null) {
1359 mLongPressListener.onLongPress(this, x, y, menuItem);
1360 }
1361 }
1362
1363 @Override
1364 public boolean onKeyDown(int keyCode, KeyEvent event) {
1365 if (KeyEvent.isConfirmKey(keyCode)) {
1366 event.startTracking();
1367 return true;
1368 }
1369 return super.onKeyDown(keyCode, event);
1370 }
1371
1372 @Override
1373 public boolean onKeyUp(int keyCode, KeyEvent event) {
1374 if (KeyEvent.isConfirmKey(keyCode)) {
1375 if (!event.isCanceled()) {
1376 performClick();
1377 }
1378 return true;
1379 }
1380 return super.onKeyUp(keyCode, event);
1381 }
1382
1383 @Override
1384 public boolean onKeyLongPress(int keyCode, KeyEvent event) {
1385 if (KeyEvent.isConfirmKey(keyCode)) {
1386 doLongClickCallback();
1387 return true;
1388 }
1389 return false;
1390 }
1391
Mady Mellor4b80b102016-01-22 08:03:58 -08001392 public void resetTranslation() {
Mady Mellor32c638a2016-09-14 08:58:25 -07001393 if (mTranslateAnim != null) {
1394 mTranslateAnim.cancel();
1395 }
Anthony Chen7acbb772017-04-07 16:45:25 -07001396
1397 if (!mShouldTranslateContents) {
1398 setTranslationX(0);
1399 } else if (mTranslateableViews != null) {
Mady Mellor4b80b102016-01-22 08:03:58 -08001400 for (int i = 0; i < mTranslateableViews.size(); i++) {
1401 mTranslateableViews.get(i).setTranslationX(0);
1402 }
Anthony Chen7acbb772017-04-07 16:45:25 -07001403 invalidateOutline();
Mady Mellor4b80b102016-01-22 08:03:58 -08001404 }
Anthony Chen7acbb772017-04-07 16:45:25 -07001405
Mady Mellor95d743c2017-01-10 12:05:27 -08001406 mMenuRow.resetMenu();
Mady Mellor4b80b102016-01-22 08:03:58 -08001407 }
1408
1409 public void animateTranslateNotification(final float leftTarget) {
1410 if (mTranslateAnim != null) {
1411 mTranslateAnim.cancel();
1412 }
Mady Mellorb0a82462016-04-30 17:31:02 -07001413 mTranslateAnim = getTranslateViewAnimator(leftTarget, null /* updateListener */);
Mady Mellor34958fa2016-02-23 09:52:17 -08001414 if (mTranslateAnim != null) {
1415 mTranslateAnim.start();
1416 }
1417 }
1418
1419 @Override
1420 public void setTranslation(float translationX) {
1421 if (areGutsExposed()) {
1422 // Don't translate if guts are showing.
1423 return;
1424 }
Anthony Chen7acbb772017-04-07 16:45:25 -07001425 if (!mShouldTranslateContents) {
1426 setTranslationX(translationX);
1427 } else if (mTranslateableViews != null) {
1428 // Translate the group of views
1429 for (int i = 0; i < mTranslateableViews.size(); i++) {
1430 if (mTranslateableViews.get(i) != null) {
1431 mTranslateableViews.get(i).setTranslationX(translationX);
1432 }
Mady Mellor34958fa2016-02-23 09:52:17 -08001433 }
Anthony Chen7acbb772017-04-07 16:45:25 -07001434 invalidateOutline();
Mady Mellor34958fa2016-02-23 09:52:17 -08001435 }
Mady Mellor95d743c2017-01-10 12:05:27 -08001436 if (mMenuRow.getMenuView() != null) {
1437 mMenuRow.onTranslationUpdate(translationX);
Mady Mellor34958fa2016-02-23 09:52:17 -08001438 }
1439 }
1440
1441 @Override
1442 public float getTranslation() {
Anthony Chene04afcb2017-04-21 13:51:32 -07001443 if (!mShouldTranslateContents) {
Anthony Chen7acbb772017-04-07 16:45:25 -07001444 return getTranslationX();
1445 }
1446
Mady Mellor34958fa2016-02-23 09:52:17 -08001447 if (mTranslateableViews != null && mTranslateableViews.size() > 0) {
1448 // All of the views in the list should have same translation, just use first one.
1449 return mTranslateableViews.get(0).getTranslationX();
1450 }
Anthony Chen7acbb772017-04-07 16:45:25 -07001451
Mady Mellor34958fa2016-02-23 09:52:17 -08001452 return 0;
1453 }
1454
1455 public Animator getTranslateViewAnimator(final float leftTarget,
1456 AnimatorUpdateListener listener) {
Mady Mellor723f1f92016-03-13 15:54:06 -07001457 if (mTranslateAnim != null) {
1458 mTranslateAnim.cancel();
1459 }
Mady Mellor34958fa2016-02-23 09:52:17 -08001460 if (areGutsExposed()) {
1461 // No translation if guts are exposed.
1462 return null;
1463 }
Mady Mellorb0a82462016-04-30 17:31:02 -07001464 final ObjectAnimator translateAnim = ObjectAnimator.ofFloat(this, TRANSLATE_CONTENT,
1465 leftTarget);
1466 if (listener != null) {
1467 translateAnim.addUpdateListener(listener);
Mady Mellor4b80b102016-01-22 08:03:58 -08001468 }
Mady Mellorb0a82462016-04-30 17:31:02 -07001469 translateAnim.addListener(new AnimatorListenerAdapter() {
1470 boolean cancelled = false;
1471
1472 @Override
1473 public void onAnimationCancel(Animator anim) {
1474 cancelled = true;
1475 }
1476
1477 @Override
1478 public void onAnimationEnd(Animator anim) {
Mady Mellor95d743c2017-01-10 12:05:27 -08001479 if (!cancelled && leftTarget == 0) {
1480 mMenuRow.resetMenu();
Mady Mellorb0a82462016-04-30 17:31:02 -07001481 mTranslateAnim = null;
1482 }
1483 }
1484 });
1485 mTranslateAnim = translateAnim;
1486 return translateAnim;
Mady Mellor4b80b102016-01-22 08:03:58 -08001487 }
1488
Selim Cinekab29aeb2015-02-20 18:18:32 +01001489 public void inflateGuts() {
1490 if (mGuts == null) {
1491 mGutsStub.inflate();
1492 }
1493 }
1494
Selim Cinekda42d652015-12-04 15:51:16 -08001495 private void updateChildrenVisibility() {
Selim Cinekd84a5932015-12-15 11:45:36 -08001496 mPrivateLayout.setVisibility(!mShowingPublic && !mIsSummaryWithChildren ? VISIBLE
1497 : INVISIBLE);
Selim Cinekef5127e2015-12-21 16:55:58 -08001498 if (mChildrenContainer != null) {
1499 mChildrenContainer.setVisibility(!mShowingPublic && mIsSummaryWithChildren ? VISIBLE
1500 : INVISIBLE);
Selim Cinekef5127e2015-12-21 16:55:58 -08001501 }
Selim Cinekda42d652015-12-04 15:51:16 -08001502 // The limits might have changed if the view suddenly became a group or vice versa
1503 updateLimits();
Selim Cinekb5605e52015-02-20 18:21:41 +01001504 }
1505
Jorim Jaggife40f7d2014-04-28 15:20:04 +02001506 @Override
Alan Viverettea54956a2015-01-07 16:05:02 -08001507 public boolean onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event) {
1508 if (super.onRequestSendAccessibilityEventInternal(child, event)) {
Jorim Jaggife40f7d2014-04-28 15:20:04 +02001509 // Add a record for the entire layout since its content is somehow small.
1510 // The event comes from a leaf view that is interacted with.
1511 AccessibilityEvent record = AccessibilityEvent.obtain();
1512 onInitializeAccessibilityEvent(record);
1513 dispatchPopulateAccessibilityEvent(record);
1514 event.appendRecord(record);
1515 return true;
1516 }
1517 return false;
Jorim Jaggic5dc0d02014-04-15 15:42:55 +02001518 }
Chris Wren51c75102013-07-16 20:49:17 -04001519
John Spurlocke15452b2014-08-21 09:44:39 -04001520 @Override
Jorim Jaggi4e857f42014-11-17 19:14:04 +01001521 public void setDark(boolean dark, boolean fade, long delay) {
1522 super.setDark(dark, fade, delay);
Lucas Dupincecc7c22017-09-12 16:02:45 -07001523 mDark = dark;
Adrian Roos28f90c72017-05-08 17:24:26 -07001524 if (!mIsHeadsUp) {
1525 // Only fade the showing view of the pulsing notification.
1526 fade = false;
1527 }
John Spurlocke15452b2014-08-21 09:44:39 -04001528 final NotificationContentView showing = getShowingLayout();
1529 if (showing != null) {
Jorim Jaggi4e857f42014-11-17 19:14:04 +01001530 showing.setDark(dark, fade, delay);
John Spurlocke15452b2014-08-21 09:44:39 -04001531 }
Selim Cinek9c7712d2015-12-08 19:19:48 -08001532 if (mIsSummaryWithChildren) {
Selim Cinekc897bd32016-03-18 17:32:31 -07001533 mChildrenContainer.setDark(dark, fade, delay);
Selim Cinek9c7712d2015-12-08 19:19:48 -08001534 }
Selim Cineka7c69632017-06-07 15:51:29 -07001535 updateShelfIconColor();
John Spurlocke15452b2014-08-21 09:44:39 -04001536 }
1537
Lucas Dupincecc7c22017-09-12 16:02:45 -07001538 /**
1539 * Tap sounds should not be played when we're unlocking.
1540 * Doing so would cause audio collision and the system would feel unpolished.
1541 */
1542 @Override
1543 public boolean isSoundEffectsEnabled() {
1544 final boolean mute = mDark && mSecureStateProvider != null &&
1545 !mSecureStateProvider.getAsBoolean();
1546 return !mute && super.isSoundEffectsEnabled();
1547 }
1548
Chris Wren51c75102013-07-16 20:49:17 -04001549 public boolean isExpandable() {
Selim Cinek388df6d2015-10-22 13:25:11 -07001550 if (mIsSummaryWithChildren && !mShowingPublic) {
1551 return !mChildrenExpanded;
1552 }
Anthony Chen6bf88a02017-04-10 14:41:44 -07001553 return mEnableNonGroupedNotificationExpand && mExpandable;
Chris Wren51c75102013-07-16 20:49:17 -04001554 }
1555
1556 public void setExpandable(boolean expandable) {
1557 mExpandable = expandable;
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001558 mPrivateLayout.updateExpandButtons(isExpandable());
Chris Wren51c75102013-07-16 20:49:17 -04001559 }
1560
Selim Cinek4ffd6362015-12-29 15:12:23 +01001561 @Override
1562 public void setClipToActualHeight(boolean clipToActualHeight) {
Selim Cinek084c16b2016-01-22 17:48:22 -08001563 super.setClipToActualHeight(clipToActualHeight || isUserLocked());
1564 getShowingLayout().setClipToActualHeight(clipToActualHeight || isUserLocked());
Selim Cinek4ffd6362015-12-29 15:12:23 +01001565 }
1566
Selim Cinek1685e632014-04-08 02:27:49 +02001567 /**
1568 * @return whether the user has changed the expansion state
1569 */
1570 public boolean hasUserChangedExpansion() {
1571 return mHasUserChangedExpansion;
1572 }
1573
Chris Wren51c75102013-07-16 20:49:17 -04001574 public boolean isUserExpanded() {
1575 return mUserExpanded;
1576 }
1577
Selim Cinek1685e632014-04-08 02:27:49 +02001578 /**
1579 * Set this notification to be expanded by the user
1580 *
1581 * @param userExpanded whether the user wants this notification to be expanded
1582 */
Chris Wren51c75102013-07-16 20:49:17 -04001583 public void setUserExpanded(boolean userExpanded) {
Selim Cinek388df6d2015-10-22 13:25:11 -07001584 setUserExpanded(userExpanded, false /* allowChildExpansion */);
1585 }
1586
1587 /**
1588 * Set this notification to be expanded by the user
1589 *
1590 * @param userExpanded whether the user wants this notification to be expanded
1591 * @param allowChildExpansion whether a call to this method allows expanding children
1592 */
1593 public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -07001594 mFalsingManager.setNotificationExpanded();
Selim Cinek414ad332017-02-24 19:06:12 -08001595 if (mIsSummaryWithChildren && !mShowingPublic && allowChildExpansion
1596 && !mChildrenContainer.showingAsLowPriority()) {
Chris Wren698b1702016-05-23 11:16:32 -04001597 final boolean wasExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
Selim Cinek388df6d2015-10-22 13:25:11 -07001598 mGroupManager.setGroupExpanded(mStatusBarNotification, userExpanded);
Selim Cinek414ad332017-02-24 19:06:12 -08001599 onExpansionChanged(true /* userAction */, wasExpanded);
Selim Cinek388df6d2015-10-22 13:25:11 -07001600 return;
1601 }
Christoph Studera7fe6312014-06-27 19:32:44 +02001602 if (userExpanded && !mExpandable) return;
Chris Wren78403d72014-07-28 10:23:24 +01001603 final boolean wasExpanded = isExpanded();
Selim Cinek1685e632014-04-08 02:27:49 +02001604 mHasUserChangedExpansion = true;
Chris Wren51c75102013-07-16 20:49:17 -04001605 mUserExpanded = userExpanded;
Selim Cinek414ad332017-02-24 19:06:12 -08001606 onExpansionChanged(true /* userAction */, wasExpanded);
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07001607 if (!wasExpanded && isExpanded()
1608 && getActualHeight() != getIntrinsicHeight()) {
1609 notifyHeightChanged(true /* needsAnimation */);
1610 }
Chris Wren51c75102013-07-16 20:49:17 -04001611 }
1612
Selim Cinekccd14fb2014-08-12 18:53:24 +02001613 public void resetUserExpansion() {
Selim Cinek414ad332017-02-24 19:06:12 -08001614 boolean changed = mUserExpanded;
Selim Cinekccd14fb2014-08-12 18:53:24 +02001615 mHasUserChangedExpansion = false;
1616 mUserExpanded = false;
Selim Cinek414ad332017-02-24 19:06:12 -08001617 if (changed && mIsSummaryWithChildren) {
1618 mChildrenContainer.onExpansionChanged();
1619 }
1620 updateShelfIconColor();
Selim Cinekccd14fb2014-08-12 18:53:24 +02001621 }
1622
Chris Wren51c75102013-07-16 20:49:17 -04001623 public boolean isUserLocked() {
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001624 return mUserLocked && !mForceUnlocked;
Chris Wren51c75102013-07-16 20:49:17 -04001625 }
1626
1627 public void setUserLocked(boolean userLocked) {
1628 mUserLocked = userLocked;
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001629 mPrivateLayout.setUserExpanding(userLocked);
Selim Cinek817abe72017-05-24 11:08:55 -07001630 // This is intentionally not guarded with mIsSummaryWithChildren since we might have had
1631 // children but not anymore.
1632 if (mChildrenContainer != null) {
Selim Cinek42357e02016-02-24 18:48:01 -08001633 mChildrenContainer.setUserLocked(userLocked);
Selim Cinek817abe72017-05-24 11:08:55 -07001634 if (mIsSummaryWithChildren && (userLocked || !isGroupExpanded())) {
Mady Mellorb0a82462016-04-30 17:31:02 -07001635 updateBackgroundForGroupState();
1636 }
Selim Cinek42357e02016-02-24 18:48:01 -08001637 }
Chris Wren51c75102013-07-16 20:49:17 -04001638 }
1639
Selim Cinek1685e632014-04-08 02:27:49 +02001640 /**
1641 * @return has the system set this notification to be expanded
1642 */
1643 public boolean isSystemExpanded() {
1644 return mIsSystemExpanded;
1645 }
1646
1647 /**
1648 * Set this notification to be expanded by the system.
1649 *
1650 * @param expand whether the system wants this notification to be expanded.
1651 */
1652 public void setSystemExpanded(boolean expand) {
Selim Cinek31094df2014-08-14 19:28:15 +02001653 if (expand != mIsSystemExpanded) {
1654 final boolean wasExpanded = isExpanded();
1655 mIsSystemExpanded = expand;
Selim Cinekb5605e52015-02-20 18:21:41 +01001656 notifyHeightChanged(false /* needsAnimation */);
Selim Cinek414ad332017-02-24 19:06:12 -08001657 onExpansionChanged(false /* userAction */, wasExpanded);
Selim Cineked6913b2016-06-01 12:01:17 -07001658 if (mIsSummaryWithChildren) {
Selim Cinekc897bd32016-03-18 17:32:31 -07001659 mChildrenContainer.updateGroupOverflow();
1660 }
Selim Cinek31094df2014-08-14 19:28:15 +02001661 }
Jorim Jaggidce3c4c2014-04-29 23:12:24 +02001662 }
1663
1664 /**
Selim Cinek83bc7832015-10-22 13:26:54 -07001665 * @param onKeyguard whether to prevent notification expansion
Jorim Jaggidce3c4c2014-04-29 23:12:24 +02001666 */
Selim Cinek83bc7832015-10-22 13:26:54 -07001667 public void setOnKeyguard(boolean onKeyguard) {
1668 if (onKeyguard != mOnKeyguard) {
Selim Cinek5cf1d052017-06-01 17:36:46 -07001669 boolean wasAboveShelf = isAboveShelf();
Selim Cinek31094df2014-08-14 19:28:15 +02001670 final boolean wasExpanded = isExpanded();
Selim Cinek83bc7832015-10-22 13:26:54 -07001671 mOnKeyguard = onKeyguard;
Selim Cinek414ad332017-02-24 19:06:12 -08001672 onExpansionChanged(false /* userAction */, wasExpanded);
Selim Cinek31094df2014-08-14 19:28:15 +02001673 if (wasExpanded != isExpanded()) {
Selim Cinekc897bd32016-03-18 17:32:31 -07001674 if (mIsSummaryWithChildren) {
1675 mChildrenContainer.updateGroupOverflow();
1676 }
Mady Mellor4b80b102016-01-22 08:03:58 -08001677 notifyHeightChanged(false /* needsAnimation */);
Selim Cinek31094df2014-08-14 19:28:15 +02001678 }
Selim Cinek5cf1d052017-06-01 17:36:46 -07001679 if (isAboveShelf() != wasAboveShelf) {
1680 mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
1681 }
Selim Cinek31094df2014-08-14 19:28:15 +02001682 }
Selim Cinek1685e632014-04-08 02:27:49 +02001683 }
1684
1685 /**
Selim Cinek9e624e72016-07-20 13:46:49 -07001686 * @return Can the underlying notification be cleared? This can be different from whether the
1687 * notification can be dismissed in case notifications are sensitive on the lockscreen.
1688 * @see #canViewBeDismissed()
Dan Sandler0d3e62f2014-07-14 17:13:50 -04001689 */
1690 public boolean isClearable() {
Selim Cinek506deb62016-07-20 15:43:59 -07001691 if (mStatusBarNotification == null || !mStatusBarNotification.isClearable()) {
1692 return false;
1693 }
1694 if (mIsSummaryWithChildren) {
1695 List<ExpandableNotificationRow> notificationChildren =
1696 mChildrenContainer.getNotificationChildren();
1697 for (int i = 0; i < notificationChildren.size(); i++) {
1698 ExpandableNotificationRow child = notificationChildren.get(i);
1699 if (!child.isClearable()) {
1700 return false;
1701 }
1702 }
1703 }
1704 return true;
Dan Sandler0d3e62f2014-07-14 17:13:50 -04001705 }
1706
Jorim Jaggi9cbadd32014-05-01 20:18:31 +02001707 @Override
1708 public int getIntrinsicHeight() {
Jorim Jaggibe565df2014-04-28 17:51:23 +02001709 if (isUserLocked()) {
1710 return getActualHeight();
1711 }
Geoffrey Pitsch4dd50062016-12-06 16:41:22 -05001712 if (mGuts != null && mGuts.isExposed()) {
Mady Mellore09fb702017-03-30 13:23:29 -07001713 return mGuts.getIntrinsicHeight();
Selim Cinekd84a5932015-12-15 11:45:36 -08001714 } else if ((isChildInGroup() && !isGroupExpanded())) {
1715 return mPrivateLayout.getMinHeight();
1716 } else if (mSensitive && mHideSensitiveForIntrinsicHeight) {
1717 return getMinHeight();
Adrian Roos6f6e1592017-05-02 16:22:53 -07001718 } else if (mIsSummaryWithChildren && (!mOnKeyguard || mShowAmbient)) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001719 return mChildrenContainer.getIntrinsicHeight();
Adrian Roos6f6e1592017-05-02 16:22:53 -07001720 } else if (isHeadsUpAllowed() && (mIsHeadsUp || mHeadsupDisappearRunning)) {
Selim Cinek73cf02a2016-06-17 13:08:00 -07001721 if (isPinned() || mHeadsupDisappearRunning) {
Selim Cinek31aada42015-12-18 17:51:15 -08001722 return getPinnedHeadsUpHeight(true /* atLeastMinHeight */);
1723 } else if (isExpanded()) {
Selim Cinekd84a5932015-12-15 11:45:36 -08001724 return Math.max(getMaxExpandHeight(), mHeadsUpHeight);
Selim Cinek8d490d42015-04-10 00:05:50 -07001725 } else {
Selim Cinek567e8452016-03-24 10:54:56 -07001726 return Math.max(getCollapsedHeight(), mHeadsUpHeight);
Selim Cinek8d490d42015-04-10 00:05:50 -07001727 }
Selim Cinek31aada42015-12-18 17:51:15 -08001728 } else if (isExpanded()) {
Selim Cinek83bc7832015-10-22 13:26:54 -07001729 return getMaxExpandHeight();
Selim Cinekd84a5932015-12-15 11:45:36 -08001730 } else {
Selim Cinek567e8452016-03-24 10:54:56 -07001731 return getCollapsedHeight();
Selim Cinek1685e632014-04-08 02:27:49 +02001732 }
Selim Cinekb5605e52015-02-20 18:21:41 +01001733 }
Selim Cinek1685e632014-04-08 02:27:49 +02001734
Adrian Roos6f6e1592017-05-02 16:22:53 -07001735 private boolean isHeadsUpAllowed() {
1736 return !mOnKeyguard && !mShowAmbient;
1737 }
1738
Mady Mellor43c2cd12016-12-12 21:05:13 -08001739 @Override
Mady Mellorb0a82462016-04-30 17:31:02 -07001740 public boolean isGroupExpanded() {
Selim Cinekeef84282015-10-30 16:28:00 -07001741 return mGroupManager.isGroupExpanded(mStatusBarNotification);
Selim Cinekb5605e52015-02-20 18:21:41 +01001742 }
1743
Selim Cinek263398f2015-10-21 17:40:23 -07001744 private void onChildrenCountChanged() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05001745 mIsSummaryWithChildren = StatusBar.ENABLE_CHILD_NOTIFICATIONS
Mady Mellorb0a82462016-04-30 17:31:02 -07001746 && mChildrenContainer != null && mChildrenContainer.getNotificationChildCount() > 0;
1747 if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() == null) {
Selim Cinek414ad332017-02-24 19:06:12 -08001748 mChildrenContainer.recreateNotificationHeader(mExpandClickListener
1749 );
Selim Cinek263398f2015-10-21 17:40:23 -07001750 }
Mady Mellor6baed9e2016-05-25 16:05:09 -07001751 getShowingLayout().updateBackgroundColor(false /* animate */);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001752 mPrivateLayout.updateExpandButtons(isExpandable());
Selim Cinekea4bef72015-12-02 15:51:10 -08001753 updateChildrenHeaderAppearance();
Selim Cinekda42d652015-12-04 15:51:16 -08001754 updateChildrenVisibility();
Selim Cinek263398f2015-10-21 17:40:23 -07001755 }
1756
Mady Mellorb0a82462016-04-30 17:31:02 -07001757 public void updateChildrenHeaderAppearance() {
Selim Cineked6913b2016-06-01 12:01:17 -07001758 if (mIsSummaryWithChildren) {
Mady Mellorb0a82462016-04-30 17:31:02 -07001759 mChildrenContainer.updateChildrenHeaderAppearance();
1760 }
1761 }
1762
Selim Cinek1685e632014-04-08 02:27:49 +02001763 /**
1764 * Check whether the view state is currently expanded. This is given by the system in {@link
1765 * #setSystemExpanded(boolean)} and can be overridden by user expansion or
1766 * collapsing in {@link #setUserExpanded(boolean)}. Note that the visual appearance of this
1767 * view can differ from this state, if layout params are modified from outside.
1768 *
1769 * @return whether the view state is currently expanded.
1770 */
Selim Cinek83bc7832015-10-22 13:26:54 -07001771 public boolean isExpanded() {
Selim Cineke81b82b2016-03-04 11:22:28 -08001772 return isExpanded(false /* allowOnKeyguard */);
1773 }
1774
1775 public boolean isExpanded(boolean allowOnKeyguard) {
1776 return (!mOnKeyguard || allowOnKeyguard)
Selim Cinekb5605e52015-02-20 18:21:41 +01001777 && (!hasUserChangedExpansion() && (isSystemExpanded() || isSystemChildExpanded())
1778 || isUserExpanded());
1779 }
1780
1781 private boolean isSystemChildExpanded() {
1782 return mIsSystemChildExpanded;
1783 }
1784
1785 public void setSystemChildExpanded(boolean expanded) {
1786 mIsSystemChildExpanded = expanded;
Selim Cinek1685e632014-04-08 02:27:49 +02001787 }
1788
Mady Mellor035badd2017-04-04 18:45:30 -07001789 public void setLayoutListener(LayoutListener listener) {
1790 mLayoutListener = listener;
1791 }
1792
1793 public void removeListener() {
1794 mLayoutListener = null;
1795 }
1796
Selim Cinek1685e632014-04-08 02:27:49 +02001797 @Override
1798 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
1799 super.onLayout(changed, left, top, right, bottom);
Selim Cinek8d490d42015-04-10 00:05:50 -07001800 updateMaxHeights();
Mady Mellor95d743c2017-01-10 12:05:27 -08001801 if (mMenuRow.getMenuView() != null) {
1802 mMenuRow.onHeightUpdate();
Mady Mellora6edc872016-04-26 11:01:03 -07001803 }
Selim Cinek875a3a12016-11-18 17:52:16 -08001804 updateContentShiftHeight();
Mady Mellor035badd2017-04-04 18:45:30 -07001805 if (mLayoutListener != null) {
1806 mLayoutListener.onLayout();
1807 }
Selim Cinek875a3a12016-11-18 17:52:16 -08001808 }
1809
1810 /**
1811 * Updates the content shift height such that the header is completely hidden when coming from
1812 * the top.
1813 */
1814 private void updateContentShiftHeight() {
Selim Cinek414ad332017-02-24 19:06:12 -08001815 NotificationHeaderView notificationHeader = getVisibleNotificationHeader();
Selim Cinek875a3a12016-11-18 17:52:16 -08001816 if (notificationHeader != null) {
1817 CachingIconView icon = notificationHeader.getIcon();
1818 mIconTransformContentShift = getRelativeTopPadding(icon) + icon.getHeight();
1819 } else {
1820 mIconTransformContentShift = mIconTransformContentShiftNoIcon;
1821 }
Selim Cinek1685e632014-04-08 02:27:49 +02001822 }
1823
Selim Cinek8d490d42015-04-10 00:05:50 -07001824 private void updateMaxHeights() {
Selim Cinekd2319fb2014-09-01 19:41:54 +02001825 int intrinsicBefore = getIntrinsicHeight();
Selim Cinek8d490d42015-04-10 00:05:50 -07001826 View expandedChild = mPrivateLayout.getExpandedChild();
1827 if (expandedChild == null) {
1828 expandedChild = mPrivateLayout.getContractedChild();
1829 }
1830 mMaxExpandHeight = expandedChild.getHeight();
1831 View headsUpChild = mPrivateLayout.getHeadsUpChild();
Selim Cinek1f3f5442015-04-10 17:54:46 -07001832 if (headsUpChild == null) {
1833 headsUpChild = mPrivateLayout.getContractedChild();
Selim Cinek8d490d42015-04-10 00:05:50 -07001834 }
Selim Cinek1f3f5442015-04-10 17:54:46 -07001835 mHeadsUpHeight = headsUpChild.getHeight();
Selim Cinekd2319fb2014-09-01 19:41:54 +02001836 if (intrinsicBefore != getIntrinsicHeight()) {
Selim Cinekbb42b7d2016-08-10 13:02:38 -07001837 notifyHeightChanged(true /* needsAnimation */);
Selim Cinekd2319fb2014-09-01 19:41:54 +02001838 }
1839 }
1840
Selim Cinekfa0a2d32016-01-14 13:02:21 -08001841 @Override
1842 public void notifyHeightChanged(boolean needsAnimation) {
1843 super.notifyHeightChanged(needsAnimation);
1844 getShowingLayout().requestSelectLayout(needsAnimation || isUserLocked());
1845 }
1846
Selim Cinek3c76d502016-02-19 15:16:33 -08001847 public void setSensitive(boolean sensitive, boolean hideSensitive) {
Jorim Jaggiae441282014-08-01 02:45:18 +02001848 mSensitive = sensitive;
Selim Cinek3c76d502016-02-19 15:16:33 -08001849 mSensitiveHiddenInGeneral = hideSensitive;
Jorim Jaggiae441282014-08-01 02:45:18 +02001850 }
1851
Mady Mellor43c2cd12016-12-12 21:05:13 -08001852 @Override
Jorim Jaggiae441282014-08-01 02:45:18 +02001853 public void setHideSensitiveForIntrinsicHeight(boolean hideSensitive) {
Selim Cinek60122be2015-04-15 18:16:50 -07001854 mHideSensitiveForIntrinsicHeight = hideSensitive;
Selim Cineka52f6a12016-02-29 15:35:58 -08001855 if (mIsSummaryWithChildren) {
1856 List<ExpandableNotificationRow> notificationChildren =
1857 mChildrenContainer.getNotificationChildren();
1858 for (int i = 0; i < notificationChildren.size(); i++) {
1859 ExpandableNotificationRow child = notificationChildren.get(i);
1860 child.setHideSensitiveForIntrinsicHeight(hideSensitive);
1861 }
1862 }
Jorim Jaggiae441282014-08-01 02:45:18 +02001863 }
1864
Mady Mellor43c2cd12016-12-12 21:05:13 -08001865 @Override
Jorim Jaggiae441282014-08-01 02:45:18 +02001866 public void setHideSensitive(boolean hideSensitive, boolean animated, long delay,
1867 long duration) {
1868 boolean oldShowingPublic = mShowingPublic;
1869 mShowingPublic = mSensitive && hideSensitive;
1870 if (mShowingPublicInitialized && mShowingPublic == oldShowingPublic) {
1871 return;
1872 }
Dan Sandlera5e0f412014-01-23 15:11:54 -05001873
1874 // bail out if no public version
Selim Cinek1685e632014-04-08 02:27:49 +02001875 if (mPublicLayout.getChildCount() == 0) return;
Dan Sandlera5e0f412014-01-23 15:11:54 -05001876
Jorim Jaggiae441282014-08-01 02:45:18 +02001877 if (!animated) {
1878 mPublicLayout.animate().cancel();
1879 mPrivateLayout.animate().cancel();
Selim Cineka554c702016-06-17 18:02:12 -07001880 if (mChildrenContainer != null) {
1881 mChildrenContainer.animate().cancel();
1882 mChildrenContainer.setAlpha(1f);
1883 }
Jorim Jaggiae441282014-08-01 02:45:18 +02001884 mPublicLayout.setAlpha(1f);
1885 mPrivateLayout.setAlpha(1f);
1886 mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE);
Selim Cinekd84a5932015-12-15 11:45:36 -08001887 updateChildrenVisibility();
Jorim Jaggiae441282014-08-01 02:45:18 +02001888 } else {
1889 animateShowingPublic(delay, duration);
1890 }
Selim Cinekc3179332016-03-04 14:44:56 -08001891 NotificationContentView showingLayout = getShowingLayout();
1892 showingLayout.updateBackgroundColor(animated);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001893 mPrivateLayout.updateExpandButtons(isExpandable());
Lucas Dupinb6ed63b2017-05-30 16:17:42 -07001894 updateShelfIconColor();
Adrian Roose5726a22016-12-19 14:26:51 -08001895 showingLayout.setDark(isDark(), false /* animate */, 0 /* delay */);
Jorim Jaggiae441282014-08-01 02:45:18 +02001896 mShowingPublicInitialized = true;
1897 }
1898
1899 private void animateShowingPublic(long delay, long duration) {
Mady Mellorb0a82462016-04-30 17:31:02 -07001900 View[] privateViews = mIsSummaryWithChildren
1901 ? new View[] {mChildrenContainer}
Selim Cinekd84a5932015-12-15 11:45:36 -08001902 : new View[] {mPrivateLayout};
1903 View[] publicViews = new View[] {mPublicLayout};
1904 View[] hiddenChildren = mShowingPublic ? privateViews : publicViews;
1905 View[] shownChildren = mShowingPublic ? publicViews : privateViews;
1906 for (final View hiddenView : hiddenChildren) {
1907 hiddenView.setVisibility(View.VISIBLE);
1908 hiddenView.animate().cancel();
1909 hiddenView.animate()
1910 .alpha(0f)
1911 .setStartDelay(delay)
1912 .setDuration(duration)
1913 .withEndAction(new Runnable() {
1914 @Override
1915 public void run() {
1916 hiddenView.setVisibility(View.INVISIBLE);
1917 }
1918 });
1919 }
1920 for (View showView : shownChildren) {
1921 showView.setVisibility(View.VISIBLE);
1922 showView.setAlpha(0f);
1923 showView.animate().cancel();
1924 showView.animate()
1925 .alpha(1f)
1926 .setStartDelay(delay)
1927 .setDuration(duration);
1928 }
Dan Sandler0d3e62f2014-07-14 17:13:50 -04001929 }
1930
Mady Mellor43c2cd12016-12-12 21:05:13 -08001931 @Override
Selim Cinek3776fe02016-02-04 13:32:43 -08001932 public boolean mustStayOnScreen() {
1933 return mIsHeadsUp;
1934 }
1935
Selim Cinek9e624e72016-07-20 13:46:49 -07001936 /**
1937 * @return Whether this view is allowed to be dismissed. Only valid for visible notifications as
1938 * otherwise some state might not be updated. To request about the general clearability
1939 * see {@link #isClearable()}.
1940 */
1941 public boolean canViewBeDismissed() {
Selim Cineke9bad242016-06-15 11:46:37 -07001942 return isClearable() && (!mShowingPublic || !mSensitiveHiddenInGeneral);
Dan Sandlera5e0f412014-01-23 15:11:54 -05001943 }
Jorim Jaggi251957d2014-04-09 04:24:09 +02001944
Ricky Waicd35def2016-05-03 11:07:07 +01001945 public void makeActionsVisibile() {
1946 setUserExpanded(true, true);
1947 if (isChildInGroup()) {
1948 mGroupManager.setGroupExpanded(mStatusBarNotification, true);
1949 }
Selim Cinekbb42b7d2016-08-10 13:02:38 -07001950 notifyHeightChanged(false /* needsAnimation */);
Ricky Waicd35def2016-05-03 11:07:07 +01001951 }
1952
Selim Cinekb5605e52015-02-20 18:21:41 +01001953 public void setChildrenExpanded(boolean expanded, boolean animate) {
1954 mChildrenExpanded = expanded;
Selim Cinek83bc7832015-10-22 13:26:54 -07001955 if (mChildrenContainer != null) {
1956 mChildrenContainer.setChildrenExpanded(expanded);
1957 }
Mady Mellor1a5d8ea2016-06-09 10:42:42 -07001958 updateBackgroundForGroupState();
Selim Cinekddf1b392016-05-27 16:33:10 -07001959 updateClickAndFocus();
Selim Cinekb5605e52015-02-20 18:21:41 +01001960 }
1961
Selim Cinekb5605e52015-02-20 18:21:41 +01001962 public static void applyTint(View v, int color) {
1963 int alpha;
1964 if (color != 0) {
1965 alpha = COLORED_DIVIDER_ALPHA;
1966 } else {
1967 color = 0xff000000;
1968 alpha = DEFAULT_DIVIDER_ALPHA;
1969 }
1970 if (v.getBackground() instanceof ColorDrawable) {
1971 ColorDrawable background = (ColorDrawable) v.getBackground();
1972 background.mutate();
1973 background.setColor(color);
1974 background.setAlpha(alpha);
1975 }
1976 }
1977
Selim Cinek1685e632014-04-08 02:27:49 +02001978 public int getMaxExpandHeight() {
Selim Cinekb5605e52015-02-20 18:21:41 +01001979 return mMaxExpandHeight;
Chris Wren51c75102013-07-16 20:49:17 -04001980 }
Jorim Jaggi584a7aa2014-04-10 23:26:13 +02001981
Mady Mellor34958fa2016-02-23 09:52:17 -08001982 public boolean areGutsExposed() {
Geoffrey Pitsch4dd50062016-12-06 16:41:22 -05001983 return (mGuts != null && mGuts.isExposed());
Mady Mellor34958fa2016-02-23 09:52:17 -08001984 }
1985
Jorim Jaggibe565df2014-04-28 17:51:23 +02001986 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001987 public boolean isContentExpandable() {
Selim Cinekc0ac4af2017-03-03 15:13:48 -08001988 if (mIsSummaryWithChildren && !mShowingPublic) {
1989 return true;
1990 }
Selim Cinek2f0df8a2014-06-10 17:40:42 +02001991 NotificationContentView showingLayout = getShowingLayout();
1992 return showingLayout.isContentExpandable();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001993 }
1994
1995 @Override
Selim Cinek560e64d2015-06-09 19:58:11 -07001996 protected View getContentView() {
Selim Cinekaa3901a2016-08-05 11:04:37 -07001997 if (mIsSummaryWithChildren && !mShowingPublic) {
Selim Cineka5703182016-05-11 21:23:16 -04001998 return mChildrenContainer;
1999 }
Selim Cinek560e64d2015-06-09 19:58:11 -07002000 return getShowingLayout();
2001 }
2002
2003 @Override
Selim Cinekaa3901a2016-08-05 11:04:37 -07002004 protected void onAppearAnimationFinished(boolean wasAppearing) {
2005 super.onAppearAnimationFinished(wasAppearing);
2006 if (wasAppearing) {
2007 // During the animation the visible view might have changed, so let's make sure all
2008 // alphas are reset
2009 if (mChildrenContainer != null) {
2010 mChildrenContainer.setAlpha(1.0f);
2011 mChildrenContainer.setLayerType(LAYER_TYPE_NONE, null);
2012 }
Adrian Rooseb434ff2017-01-11 11:18:48 -08002013 for (NotificationContentView l : mLayouts) {
2014 l.setAlpha(1.0f);
2015 l.setLayerType(LAYER_TYPE_NONE, null);
2016 }
Selim Cinekaa3901a2016-08-05 11:04:37 -07002017 }
2018 }
2019
2020 @Override
Mady Mellorb0a82462016-04-30 17:31:02 -07002021 public int getExtraBottomPadding() {
2022 if (mIsSummaryWithChildren && isGroupExpanded()) {
2023 return mIncreasedPaddingBetweenElements;
2024 }
2025 return 0;
2026 }
2027
2028 @Override
Jorim Jaggid552d9d2014-05-07 19:41:13 +02002029 public void setActualHeight(int height, boolean notifyListeners) {
Selim Cinek4ca6c632017-02-23 18:03:37 -08002030 boolean changed = height != getActualHeight();
Selim Cinekb5605e52015-02-20 18:21:41 +01002031 super.setActualHeight(height, notifyListeners);
Selim Cinek4ca6c632017-02-23 18:03:37 -08002032 if (changed && isRemoved()) {
2033 // TODO: remove this once we found the gfx bug for this.
2034 // This is a hack since a removed view sometimes would just stay blank. it occured
2035 // when sending yourself a message and then clicking on it.
2036 ViewGroup parent = (ViewGroup) getParent();
2037 if (parent != null) {
2038 parent.invalidate();
2039 }
2040 }
Geoffrey Pitsch4dd50062016-12-06 16:41:22 -05002041 if (mGuts != null && mGuts.isExposed()) {
Mady Mellorb53bc272016-02-11 18:28:23 -08002042 mGuts.setActualHeight(height);
2043 return;
2044 }
Selim Cinekeef84282015-10-30 16:28:00 -07002045 int contentHeight = Math.max(getMinHeight(), height);
Adrian Rooseb434ff2017-01-11 11:18:48 -08002046 for (NotificationContentView l : mLayouts) {
2047 l.setContentHeight(contentHeight);
2048 }
Selim Cinek42357e02016-02-24 18:48:01 -08002049 if (mIsSummaryWithChildren) {
2050 mChildrenContainer.setActualHeight(height);
2051 }
Jorim Jaggib1cd3c12014-09-08 19:55:17 +02002052 if (mGuts != null) {
2053 mGuts.setActualHeight(height);
2054 }
Mady Mellor54540972017-06-07 11:55:36 -07002055 if (mMenuRow.getMenuView() != null) {
2056 mMenuRow.onHeightUpdate();
2057 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02002058 }
2059
2060 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01002061 public int getMaxContentHeight() {
Selim Cinek83bc7832015-10-22 13:26:54 -07002062 if (mIsSummaryWithChildren && !mShowingPublic) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08002063 return mChildrenContainer.getMaxContentHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -07002064 }
Selim Cinek2f0df8a2014-06-10 17:40:42 +02002065 NotificationContentView showingLayout = getShowingLayout();
2066 return showingLayout.getMaxHeight();
Jorim Jaggibe565df2014-04-28 17:51:23 +02002067 }
2068
2069 @Override
Selim Cinekeb3fc3d2017-09-15 13:37:14 -07002070 public int getMinHeight(boolean ignoreTemporaryStates) {
2071 if (!ignoreTemporaryStates && mGuts != null && mGuts.isExposed()) {
Mady Mellore09fb702017-03-30 13:23:29 -07002072 return mGuts.getIntrinsicHeight();
Selim Cinekeb3fc3d2017-09-15 13:37:14 -07002073 } else if (!ignoreTemporaryStates && isHeadsUpAllowed() && mIsHeadsUp
2074 && mHeadsUpManager.isTrackingHeadsUp()) {
Selim Cinek31aada42015-12-18 17:51:15 -08002075 return getPinnedHeadsUpHeight(false /* atLeastMinHeight */);
2076 } else if (mIsSummaryWithChildren && !isGroupExpanded() && !mShowingPublic) {
Selim Cinekb55386d2015-12-16 17:26:49 -08002077 return mChildrenContainer.getMinHeight();
Selim Cinekeb3fc3d2017-09-15 13:37:14 -07002078 } else if (!ignoreTemporaryStates && isHeadsUpAllowed() && mIsHeadsUp) {
Selim Cinek31aada42015-12-18 17:51:15 -08002079 return mHeadsUpHeight;
Selim Cinekb55386d2015-12-16 17:26:49 -08002080 }
Selim Cinek816c8e42015-11-19 12:00:45 -08002081 NotificationContentView showingLayout = getShowingLayout();
2082 return showingLayout.getMinHeight();
2083 }
2084
2085 @Override
Selim Cinek567e8452016-03-24 10:54:56 -07002086 public int getCollapsedHeight() {
Selim Cinek2c584612016-02-29 16:14:25 -08002087 if (mIsSummaryWithChildren && !mShowingPublic) {
Selim Cinek567e8452016-03-24 10:54:56 -07002088 return mChildrenContainer.getCollapsedHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -07002089 }
Selim Cinek816c8e42015-11-19 12:00:45 -08002090 return getMinHeight();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02002091 }
2092
2093 @Override
Jorim Jaggibe565df2014-04-28 17:51:23 +02002094 public void setClipTopAmount(int clipTopAmount) {
2095 super.setClipTopAmount(clipTopAmount);
Adrian Rooseb434ff2017-01-11 11:18:48 -08002096 for (NotificationContentView l : mLayouts) {
2097 l.setClipTopAmount(clipTopAmount);
2098 }
Jorim Jaggib1cd3c12014-09-08 19:55:17 +02002099 if (mGuts != null) {
2100 mGuts.setClipTopAmount(clipTopAmount);
2101 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02002102 }
2103
Selim Cineka686b2c2016-10-26 13:58:27 -07002104 @Override
2105 public void setClipBottomAmount(int clipBottomAmount) {
Selim Cinek65d418e2016-11-29 15:42:34 -08002106 if (clipBottomAmount != mClipBottomAmount) {
2107 super.setClipBottomAmount(clipBottomAmount);
Adrian Rooseb434ff2017-01-11 11:18:48 -08002108 for (NotificationContentView l : mLayouts) {
2109 l.setClipBottomAmount(clipBottomAmount);
2110 }
Selim Cinek65d418e2016-11-29 15:42:34 -08002111 if (mGuts != null) {
2112 mGuts.setClipBottomAmount(clipBottomAmount);
2113 }
Selim Cineka686b2c2016-10-26 13:58:27 -07002114 }
Selim Cinekb3dadcc2016-11-21 17:21:13 -08002115 if (mChildrenContainer != null) {
Selim Cinek65d418e2016-11-29 15:42:34 -08002116 // We have to update this even if it hasn't changed, since the children locations can
2117 // have changed
Selim Cinekb3dadcc2016-11-21 17:21:13 -08002118 mChildrenContainer.setClipBottomAmount(clipBottomAmount);
2119 }
Selim Cineka686b2c2016-10-26 13:58:27 -07002120 }
2121
Selim Cinek31094df2014-08-14 19:28:15 +02002122 public boolean isMaxExpandHeightInitialized() {
2123 return mMaxExpandHeight != 0;
Selim Cinek7d447722014-06-10 15:51:59 +02002124 }
Selim Cinek2f0df8a2014-06-10 17:40:42 +02002125
Selim Cinek42357e02016-02-24 18:48:01 -08002126 public NotificationContentView getShowingLayout() {
Selim Cinek2f0df8a2014-06-10 17:40:42 +02002127 return mShowingPublic ? mPublicLayout : mPrivateLayout;
2128 }
Chris Wren78403d72014-07-28 10:23:24 +01002129
Selim Cinek1a48bab2017-02-17 19:38:40 -08002130 public void setLegacy(boolean legacy) {
Adrian Rooseb434ff2017-01-11 11:18:48 -08002131 for (NotificationContentView l : mLayouts) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08002132 l.setLegacy(legacy);
Adrian Rooseb434ff2017-01-11 11:18:48 -08002133 }
Jorim Jaggi59ec3042015-06-05 15:18:43 -07002134 }
2135
Selim Cineka6c6bfb2015-10-29 16:27:08 -07002136 @Override
2137 protected void updateBackgroundTint() {
2138 super.updateBackgroundTint();
Mady Mellorb0a82462016-04-30 17:31:02 -07002139 updateBackgroundForGroupState();
Selim Cineka6c6bfb2015-10-29 16:27:08 -07002140 if (mIsSummaryWithChildren) {
2141 List<ExpandableNotificationRow> notificationChildren =
2142 mChildrenContainer.getNotificationChildren();
2143 for (int i = 0; i < notificationChildren.size(); i++) {
2144 ExpandableNotificationRow child = notificationChildren.get(i);
Mady Mellorb0a82462016-04-30 17:31:02 -07002145 child.updateBackgroundForGroupState();
Selim Cineka6c6bfb2015-10-29 16:27:08 -07002146 }
2147 }
2148 }
2149
Mady Mellorb0a82462016-04-30 17:31:02 -07002150 /**
2151 * Called when a group has finished animating from collapsed or expanded state.
2152 */
2153 public void onFinishedExpansionChange() {
2154 mGroupExpansionChanging = false;
2155 updateBackgroundForGroupState();
2156 }
2157
2158 /**
2159 * Updates the parent and children backgrounds in a group based on the expansion state.
2160 */
2161 public void updateBackgroundForGroupState() {
2162 if (mIsSummaryWithChildren) {
2163 // Only when the group has finished expanding do we hide its background.
Anthony Chen6bf88a02017-04-10 14:41:44 -07002164 mShowNoBackground = !mShowGroupBackgroundWhenExpanded && isGroupExpanded()
2165 && !isGroupExpansionChanging() && !isUserLocked();
Mady Mellorb0a82462016-04-30 17:31:02 -07002166 mChildrenContainer.updateHeaderForExpansion(mShowNoBackground);
2167 List<ExpandableNotificationRow> children = mChildrenContainer.getNotificationChildren();
2168 for (int i = 0; i < children.size(); i++) {
2169 children.get(i).updateBackgroundForGroupState();
2170 }
2171 } else if (isChildInGroup()) {
2172 final int childColor = getShowingLayout().getBackgroundColorForExpansionState();
2173 // Only show a background if the group is expanded OR if it is expanding / collapsing
Anthony Chen6bf88a02017-04-10 14:41:44 -07002174 // and has a custom background color.
Mady Mellorb0a82462016-04-30 17:31:02 -07002175 final boolean showBackground = isGroupExpanded()
2176 || ((mNotificationParent.isGroupExpansionChanging()
Anthony Chen6bf88a02017-04-10 14:41:44 -07002177 || mNotificationParent.isUserLocked()) && childColor != 0);
Mady Mellorb0a82462016-04-30 17:31:02 -07002178 mShowNoBackground = !showBackground;
2179 } else {
2180 // Only children or parents ever need no background.
2181 mShowNoBackground = false;
2182 }
2183 updateOutline();
Selim Cineka6c6bfb2015-10-29 16:27:08 -07002184 updateBackground();
2185 }
2186
Adrian Roos4a579672016-05-24 16:54:37 -07002187 public int getPositionOfChild(ExpandableNotificationRow childRow) {
2188 if (mIsSummaryWithChildren) {
2189 return mChildrenContainer.getPositionInLinearLayout(childRow);
2190 }
2191 return 0;
2192 }
2193
Chris Wren78403d72014-07-28 10:23:24 +01002194 public void setExpansionLogger(ExpansionLogger logger, String key) {
2195 mLogger = logger;
2196 mLoggingKey = key;
2197 }
2198
Chris Wren6abeeb92016-05-26 14:44:38 -04002199 public void onExpandedByGesture(boolean userExpanded) {
2200 int event = MetricsEvent.ACTION_NOTIFICATION_GESTURE_EXPANDER;
2201 if (mGroupManager.isSummaryOfGroup(getStatusBarNotification())) {
2202 event = MetricsEvent.ACTION_NOTIFICATION_GROUP_GESTURE_EXPANDER;
2203 }
2204 MetricsLogger.action(mContext, event, userExpanded);
2205 }
2206
Selim Cinek6183d122016-01-14 18:48:41 -08002207 @Override
Selim Cinek42357e02016-02-24 18:48:01 -08002208 public float getIncreasedPaddingAmount() {
2209 if (mIsSummaryWithChildren) {
2210 if (isGroupExpanded()) {
2211 return 1.0f;
2212 } else if (isUserLocked()) {
Selim Cinek414ad332017-02-24 19:06:12 -08002213 return mChildrenContainer.getIncreasedPaddingAmount();
Selim Cinek42357e02016-02-24 18:48:01 -08002214 }
Selim Cinek99104832017-01-25 14:47:33 -08002215 } else if (isColorized() && (!mIsLowPriority || isExpanded())) {
Selim Cineka7ed2c12017-01-23 20:47:24 -08002216 return -1.0f;
Selim Cinek42357e02016-02-24 18:48:01 -08002217 }
2218 return 0.0f;
Selim Cinek61633a82016-01-25 15:54:10 -08002219 }
2220
Selim Cineka7ed2c12017-01-23 20:47:24 -08002221 private boolean isColorized() {
Selim Cinek99104832017-01-25 14:47:33 -08002222 return mIsColorized && mBgTint != NO_COLOR;
Selim Cineka7ed2c12017-01-23 20:47:24 -08002223 }
2224
Selim Cinek61633a82016-01-25 15:54:10 -08002225 @Override
Selim Cinek6183d122016-01-14 18:48:41 -08002226 protected boolean disallowSingleClick(MotionEvent event) {
2227 float x = event.getX();
2228 float y = event.getY();
Selim Cinek34eda5e2016-02-18 17:10:43 -08002229 NotificationHeaderView header = getVisibleNotificationHeader();
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07002230 if (header != null && header.isInTouchRect(x - getTranslation(), y)) {
2231 return true;
2232 }
2233 if ((!mIsSummaryWithChildren || mShowingPublic)
2234 && getShowingLayout().disallowSingleClick(x, y)) {
2235 return true;
Selim Cinek6183d122016-01-14 18:48:41 -08002236 }
2237 return super.disallowSingleClick(event);
2238 }
2239
Selim Cinek414ad332017-02-24 19:06:12 -08002240 private void onExpansionChanged(boolean userAction, boolean wasExpanded) {
Chris Wren698b1702016-05-23 11:16:32 -04002241 boolean nowExpanded = isExpanded();
Selim Cinek414ad332017-02-24 19:06:12 -08002242 if (mIsSummaryWithChildren && (!mIsLowPriority || wasExpanded)) {
Chris Wren698b1702016-05-23 11:16:32 -04002243 nowExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
2244 }
Selim Cinek414ad332017-02-24 19:06:12 -08002245 if (nowExpanded != wasExpanded) {
2246 updateShelfIconColor();
2247 if (mLogger != null) {
2248 mLogger.logNotificationExpansion(mLoggingKey, userAction, nowExpanded);
2249 }
2250 if (mIsSummaryWithChildren) {
2251 mChildrenContainer.onExpansionChanged();
2252 }
Chris Wren78403d72014-07-28 10:23:24 +01002253 }
2254 }
Selim Cinek570981d2015-12-01 11:37:01 -08002255
Selim Cineke9bad242016-06-15 11:46:37 -07002256 @Override
2257 public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
2258 super.onInitializeAccessibilityNodeInfoInternal(info);
Geoffrey Pitsch409db272017-08-28 14:51:52 +00002259 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
Selim Cineke9bad242016-06-15 11:46:37 -07002260 if (canViewBeDismissed()) {
2261 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS);
2262 }
Selim Cinekc0ac4af2017-03-03 15:13:48 -08002263 boolean expandable = mShowingPublic;
2264 boolean isExpanded = false;
2265 if (!expandable) {
2266 if (mIsSummaryWithChildren) {
2267 expandable = true;
2268 if (!mIsLowPriority || isExpanded()) {
2269 isExpanded = isGroupExpanded();
2270 }
2271 } else {
2272 expandable = mPrivateLayout.isContentExpandable();
2273 isExpanded = isExpanded();
2274 }
2275 }
2276 if (expandable) {
2277 if (isExpanded) {
2278 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE);
2279 } else {
2280 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND);
2281 }
2282 }
Selim Cineke9bad242016-06-15 11:46:37 -07002283 }
2284
2285 @Override
2286 public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
2287 if (super.performAccessibilityActionInternal(action, arguments)) {
2288 return true;
2289 }
2290 switch (action) {
2291 case AccessibilityNodeInfo.ACTION_DISMISS:
2292 NotificationStackScrollLayout.performDismiss(this, mGroupManager,
2293 true /* fromAccessibility */);
2294 return true;
Selim Cinekc0ac4af2017-03-03 15:13:48 -08002295 case AccessibilityNodeInfo.ACTION_COLLAPSE:
2296 case AccessibilityNodeInfo.ACTION_EXPAND:
2297 mExpandClickListener.onClick(this);
2298 return true;
Geoffrey Pitsch409db272017-08-28 14:51:52 +00002299 case AccessibilityNodeInfo.ACTION_LONG_CLICK:
2300 doLongClickCallback();
2301 return true;
Selim Cineke9bad242016-06-15 11:46:37 -07002302 }
2303 return false;
2304 }
2305
2306 public boolean shouldRefocusOnDismiss() {
2307 return mRefocusOnDismiss || isAccessibilityFocused();
2308 }
2309
Selim Cinek570981d2015-12-01 11:37:01 -08002310 public interface OnExpandClickListener {
Selim Cinek31aada42015-12-18 17:51:15 -08002311 void onExpandClicked(NotificationData.Entry clickedEntry, boolean nowExpanded);
Selim Cinek570981d2015-12-01 11:37:01 -08002312 }
Selim Cinekbbcebde2016-11-09 18:28:20 -08002313
2314 @Override
2315 public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
2316 return new NotificationViewState(stackScrollState);
2317 }
2318
Selim Cinekd127d792016-11-01 19:11:41 -07002319 @Override
2320 public boolean isAboveShelf() {
Selim Cinekc8007c52017-02-28 16:09:56 -08002321 return !isOnKeyguard()
2322 && (mIsPinned || mHeadsupDisappearRunning || (mIsHeadsUp && mAboveShelf));
Selim Cinekd127d792016-11-01 19:11:41 -07002323 }
2324
Adrian Roos0aac04f2016-12-08 15:59:29 -08002325 public void setShowAmbient(boolean showAmbient) {
2326 if (showAmbient != mShowAmbient) {
2327 mShowAmbient = showAmbient;
Adrian Roos6f6e1592017-05-02 16:22:53 -07002328 if (mChildrenContainer != null) {
2329 mChildrenContainer.notifyShowAmbientChanged();
2330 }
Adrian Roos0aac04f2016-12-08 15:59:29 -08002331 notifyHeightChanged(false /* needsAnimation */);
2332 }
2333 }
2334
Adrian Roos6f6e1592017-05-02 16:22:53 -07002335 public boolean isShowingAmbient() {
2336 return mShowAmbient;
2337 }
2338
Selim Cinekd127d792016-11-01 19:11:41 -07002339 public void setAboveShelf(boolean aboveShelf) {
Selim Cinek5cf1d052017-06-01 17:36:46 -07002340 boolean wasAboveShelf = isAboveShelf();
Selim Cinekd127d792016-11-01 19:11:41 -07002341 mAboveShelf = aboveShelf;
Selim Cinek5cf1d052017-06-01 17:36:46 -07002342 if (isAboveShelf() != wasAboveShelf) {
2343 mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
2344 }
Selim Cinekd127d792016-11-01 19:11:41 -07002345 }
2346
Selim Cinekd4776a52017-02-14 18:50:16 -08002347 public static class NotificationViewState extends ExpandableViewState {
Selim Cinekbbcebde2016-11-09 18:28:20 -08002348
2349 private final StackScrollState mOverallState;
Selim Cinek0242fbb2016-10-19 13:38:32 -07002350
Selim Cinekbbcebde2016-11-09 18:28:20 -08002351
2352 private NotificationViewState(StackScrollState stackScrollState) {
2353 mOverallState = stackScrollState;
2354 }
2355
2356 @Override
2357 public void applyToView(View view) {
2358 super.applyToView(view);
2359 if (view instanceof ExpandableNotificationRow) {
2360 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinekbbcebde2016-11-09 18:28:20 -08002361 row.applyChildrenState(mOverallState);
2362 }
2363 }
Selim Cinek0cfbef42016-11-09 19:06:36 -08002364
2365 @Override
Selim Cinek2b549f42016-11-22 16:38:51 -08002366 protected void onYTranslationAnimationFinished(View view) {
2367 super.onYTranslationAnimationFinished(view);
Selim Cinekd4776a52017-02-14 18:50:16 -08002368 if (view instanceof ExpandableNotificationRow) {
2369 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
2370 if (row.isHeadsUpAnimatingAway()) {
2371 row.setHeadsUpAnimatingAway(false);
2372 }
Selim Cinek0cfbef42016-11-09 19:06:36 -08002373 }
2374 }
2375
2376 @Override
2377 public void animateTo(View child, AnimationProperties properties) {
2378 super.animateTo(child, properties);
2379 if (child instanceof ExpandableNotificationRow) {
2380 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2381 row.startChildAnimation(mOverallState, properties);
2382 }
2383 }
Selim Cinekbbcebde2016-11-09 18:28:20 -08002384 }
Selim Cinek817abe72017-05-24 11:08:55 -07002385
2386 @VisibleForTesting
2387 protected void setChildrenContainer(NotificationChildrenContainer childrenContainer) {
2388 mChildrenContainer = childrenContainer;
2389 }
Geoffrey Pitsch409db272017-08-28 14:51:52 +00002390
2391 /**
2392 * Equivalent to View.OnLongClickListener with coordinates
2393 */
2394 public interface LongPressListener {
2395 /**
2396 * Equivalent to {@link View.OnLongClickListener#onLongClick(View)} with coordinates
2397 * @return whether the longpress was handled
2398 */
2399 boolean onLongPress(View v, int x, int y, MenuItem item);
2400 }
Chris Wren51c75102013-07-16 20:49:17 -04002401}