blob: 2a376783a0d6c7122837c9e1ebb485b5772856c6 [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 Cinek2627d722018-01-19 12:16:49 -080019import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
Selim Cinek5ba22542017-04-20 15:16:10 -070020import static com.android.systemui.statusbar.notification.NotificationInflater.InflationCallback;
Selim Cinekc478f902017-02-22 20:55:44 -080021
Mady Mellor4b80b102016-01-22 08:03:58 -080022import android.animation.Animator;
23import android.animation.AnimatorListenerAdapter;
Mady Mellor4b80b102016-01-22 08:03:58 -080024import android.animation.ObjectAnimator;
Mady Mellor4b80b102016-01-22 08:03:58 -080025import android.animation.ValueAnimator.AnimatorUpdateListener;
Selim Cinekddf1b392016-05-27 16:33:10 -070026import android.annotation.Nullable;
Rohan Shah63411fc2018-03-28 19:05:52 -070027import android.app.NotificationChannel;
Chris Wren51c75102013-07-16 20:49:17 -040028import android.content.Context;
Rohan Shah63411fc2018-03-28 19:05:52 -070029import android.content.pm.PackageInfo;
30import android.content.pm.PackageManager;
Mady Mellor9d03a522017-04-04 18:45:30 -070031import android.content.res.Configuration;
Julia Reynoldsb5867452018-02-28 16:31:35 -050032import android.content.res.Resources;
Selim Cinek515b2032017-11-15 10:20:19 -080033import android.graphics.Path;
Selim Cinekcab4a602014-09-03 14:47:57 +020034import android.graphics.drawable.AnimatedVectorDrawable;
35import android.graphics.drawable.AnimationDrawable;
Selim Cinekb5605e52015-02-20 18:21:41 +010036import android.graphics.drawable.ColorDrawable;
Selim Cinekcab4a602014-09-03 14:47:57 +020037import android.graphics.drawable.Drawable;
Rohan Shah4ed1b2a2018-03-30 13:26:01 -070038import android.os.AsyncTask;
Selim Cinekda42d652015-12-04 15:51:16 -080039import android.os.Build;
Selim Cineke9bad242016-06-15 11:46:37 -070040import android.os.Bundle;
Jorim Jaggib1cd3c12014-09-08 19:55:17 +020041import android.service.notification.StatusBarNotification;
Julia Reynoldsfc640012018-02-21 12:25:27 -050042import android.util.ArraySet;
Chris Wren51c75102013-07-16 20:49:17 -040043import android.util.AttributeSet;
Mady Mellorb0a82462016-04-30 17:31:02 -070044import android.util.FloatProperty;
Rohan Shah63411fc2018-03-28 19:05:52 -070045import android.util.Log;
Selim Cinek2627d722018-01-19 12:16:49 -080046import android.util.MathUtils;
Mady Mellorb0a82462016-04-30 17:31:02 -070047import android.util.Property;
Geoffrey Pitsch409db272017-08-28 14:51:52 +000048import android.view.KeyEvent;
Selim Cinek01af3342016-02-09 19:25:31 -080049import android.view.LayoutInflater;
Selim Cinek6183d122016-01-14 18:48:41 -080050import android.view.MotionEvent;
Selim Cinekeaa29ca2015-11-23 13:51:13 -080051import android.view.NotificationHeaderView;
Dan Sandlera5e0f412014-01-23 15:11:54 -050052import android.view.View;
Selim Cinek875a3a12016-11-18 17:52:16 -080053import android.view.ViewGroup;
Jorim Jaggib1cd3c12014-09-08 19:55:17 +020054import android.view.ViewStub;
Jorim Jaggife40f7d2014-04-28 15:20:04 +020055import android.view.accessibility.AccessibilityEvent;
Selim Cineke9bad242016-06-15 11:46:37 -070056import android.view.accessibility.AccessibilityNodeInfo;
Mady Mellorb3a6aed2018-05-11 13:24:07 -070057import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
Selim Cinek98713a42015-09-21 15:47:20 +020058import android.widget.Chronometer;
Mady Mellor95d743c2017-01-10 12:05:27 -080059import android.widget.FrameLayout;
Selim Cinekcab4a602014-09-03 14:47:57 +020060import android.widget.ImageView;
Selim Cinek1a48bab2017-02-17 19:38:40 -080061import android.widget.RemoteViews;
Selim Cinekb5605e52015-02-20 18:21:41 +010062
Selim Cinek5ba22542017-04-20 15:16:10 -070063import com.android.internal.annotations.VisibleForTesting;
Chris Wren698b1702016-05-23 11:16:32 -040064import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010065import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Selim Cinek4bb59342016-04-08 19:29:35 -070066import com.android.internal.util.NotificationColorUtil;
Selim Cinek875a3a12016-11-18 17:52:16 -080067import com.android.internal.widget.CachingIconView;
Mady Mellor95d743c2017-01-10 12:05:27 -080068import com.android.systemui.Dependency;
Selim Cinek0242fbb2016-10-19 13:38:32 -070069import com.android.systemui.Interpolators;
Dan Sandlera5e0f412014-01-23 15:11:54 -050070import com.android.systemui.R;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070071import com.android.systemui.classifier.FalsingManager;
Mady Mellor95d743c2017-01-10 12:05:27 -080072import com.android.systemui.plugins.PluginListener;
73import com.android.systemui.plugins.PluginManager;
74import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
75import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
76import com.android.systemui.statusbar.NotificationGuts.GutsContent;
Selim Cinek5cf1d052017-06-01 17:36:46 -070077import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
Selim Cinek2627d722018-01-19 12:16:49 -080078import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
Selim Cinekc897bd32016-03-18 17:32:31 -070079import com.android.systemui.statusbar.notification.HybridNotificationView;
Rohan Shahda5dcdd2018-04-27 17:21:50 -070080import com.android.systemui.statusbar.notification.NotificationCounters;
Selim Cinek1a48bab2017-02-17 19:38:40 -080081import com.android.systemui.statusbar.notification.NotificationInflater;
Selim Cinek6743c0b2017-01-18 18:24:01 -080082import com.android.systemui.statusbar.notification.NotificationUtils;
Selim Cineke62255c2017-09-28 18:23:23 -070083import com.android.systemui.statusbar.notification.NotificationViewWrapper;
Selim Cineka7d4f822016-12-06 14:34:47 -080084import com.android.systemui.statusbar.notification.VisualStabilityManager;
Selim Cinekb5605e52015-02-20 18:21:41 +010085import com.android.systemui.statusbar.phone.NotificationGroupManager;
Jason Monk2a6ea9c2017-01-26 11:14:51 -050086import com.android.systemui.statusbar.phone.StatusBar;
Selim Cinek31aada42015-12-18 17:51:15 -080087import com.android.systemui.statusbar.policy.HeadsUpManager;
Selim Cinek2627d722018-01-19 12:16:49 -080088import com.android.systemui.statusbar.stack.AmbientState;
Selim Cinek0cfbef42016-11-09 19:06:36 -080089import com.android.systemui.statusbar.stack.AnimationProperties;
90import com.android.systemui.statusbar.stack.ExpandableViewState;
Selim Cinekb5605e52015-02-20 18:21:41 +010091import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
92import com.android.systemui.statusbar.stack.StackScrollState;
Selim Cinekb5605e52015-02-20 18:21:41 +010093
Mady Mellor4b80b102016-01-22 08:03:58 -080094import java.util.ArrayList;
Selim Cinekb5605e52015-02-20 18:21:41 +010095import java.util.List;
Lucas Dupincecc7c22017-09-12 16:02:45 -070096import java.util.function.BooleanSupplier;
Selim Cinek8875de12018-03-22 10:14:32 -070097import java.util.function.Consumer;
Dan Sandlera5e0f412014-01-23 15:11:54 -050098
Rohan Shah63411fc2018-03-28 19:05:52 -070099/**
100 * View representing a notification item - this can be either the individual child notification or
101 * the group summary (which contains 1 or more child notifications).
102 */
Mady Mellor95d743c2017-01-10 12:05:27 -0800103public class ExpandableNotificationRow extends ActivatableNotificationView
104 implements PluginListener<NotificationMenuRowPlugin> {
Selim Cinekb5605e52015-02-20 18:21:41 +0100105
Rohan Shah4ed1b2a2018-03-30 13:26:01 -0700106 private static final boolean DEBUG = false;
Selim Cinekb5605e52015-02-20 18:21:41 +0100107 private static final int DEFAULT_DIVIDER_ALPHA = 0x29;
108 private static final int COLORED_DIVIDER_ALPHA = 0x7B;
Mady Mellor95d743c2017-01-10 12:05:27 -0800109 private static final int MENU_VIEW_INDEX = 0;
Rohan Shah63411fc2018-03-28 19:05:52 -0700110 private static final String TAG = "ExpandableNotifRow";
felkachang08579552018-05-24 15:38:04 +0800111 public static final float DEFAULT_HEADER_VISIBLE_AMOUNT = 1.0f;
Mady Mellor95d743c2017-01-10 12:05:27 -0800112
Rohan Shah4ed1b2a2018-03-30 13:26:01 -0700113 /**
114 * Listener for when {@link ExpandableNotificationRow} is laid out.
115 */
Mady Mellor035badd2017-04-04 18:45:30 -0700116 public interface LayoutListener {
Rohan Shah4ed1b2a2018-03-30 13:26:01 -0700117 void onLayout();
Mady Mellor035badd2017-04-04 18:45:30 -0700118 }
119
120 private LayoutListener mLayoutListener;
Lucas Dupincecc7c22017-09-12 16:02:45 -0700121 private boolean mDark;
Selim Cinek55a3e732017-05-25 18:30:10 -0700122 private boolean mLowPriorityStateUpdated;
Selim Cinek1a48bab2017-02-17 19:38:40 -0800123 private final NotificationInflater mNotificationInflater;
Selim Cinek0242fbb2016-10-19 13:38:32 -0700124 private int mIconTransformContentShift;
Selim Cinek875a3a12016-11-18 17:52:16 -0800125 private int mIconTransformContentShiftNoIcon;
Selim Cinek01af3342016-02-09 19:25:31 -0800126 private int mNotificationMinHeightLegacy;
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800127 private int mNotificationMinHeightBeforeP;
Selim Cinek01af3342016-02-09 19:25:31 -0800128 private int mMaxHeadsUpHeightLegacy;
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800129 private int mMaxHeadsUpHeightBeforeP;
Selim Cinek01af3342016-02-09 19:25:31 -0800130 private int mMaxHeadsUpHeight;
Selim Cinek87ed69b2017-02-09 15:59:43 -0800131 private int mMaxHeadsUpHeightIncreased;
Selim Cinek01af3342016-02-09 19:25:31 -0800132 private int mNotificationMinHeight;
Selim Cinek7d1009b2017-01-25 15:28:28 -0800133 private int mNotificationMinHeightLarge;
Selim Cinek01af3342016-02-09 19:25:31 -0800134 private int mNotificationMaxHeight;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800135 private int mNotificationAmbientHeight;
Mady Mellorb0a82462016-04-30 17:31:02 -0700136 private int mIncreasedPaddingBetweenElements;
Selim Cinek2627d722018-01-19 12:16:49 -0800137 private int mNotificationLaunchHeight;
Selim Cinek9b9d6e12017-11-30 12:29:47 +0100138 private boolean mMustStayOnScreen;
Chris Wren51c75102013-07-16 20:49:17 -0400139
Selim Cinek1685e632014-04-08 02:27:49 +0200140 /** Does this row contain layouts that can adapt to row expansion */
Chris Wren51c75102013-07-16 20:49:17 -0400141 private boolean mExpandable;
Selim Cinek1685e632014-04-08 02:27:49 +0200142 /** Has the user actively changed the expansion state of this row */
143 private boolean mHasUserChangedExpansion;
144 /** If {@link #mHasUserChangedExpansion}, has the user expanded this row */
Chris Wren51c75102013-07-16 20:49:17 -0400145 private boolean mUserExpanded;
Rohan Shah524cf7b2018-03-15 14:40:02 -0700146 /** Whether the blocking helper is showing on this notification (even if dismissed) */
147 private boolean mIsBlockingHelperShowing;
Selim Cinek31aada42015-12-18 17:51:15 -0800148
149 /**
150 * Has this notification been expanded while it was pinned
151 */
152 private boolean mExpandedWhenPinned;
Selim Cinek1685e632014-04-08 02:27:49 +0200153 /** Is the user touching this row */
Chris Wren51c75102013-07-16 20:49:17 -0400154 private boolean mUserLocked;
Selim Cinek1685e632014-04-08 02:27:49 +0200155 /** Are we showing the "public" version */
Dan Sandlera5e0f412014-01-23 15:11:54 -0500156 private boolean mShowingPublic;
Jorim Jaggiae441282014-08-01 02:45:18 +0200157 private boolean mSensitive;
Selim Cinek3c76d502016-02-19 15:16:33 -0800158 private boolean mSensitiveHiddenInGeneral;
Jorim Jaggiae441282014-08-01 02:45:18 +0200159 private boolean mShowingPublicInitialized;
Selim Cinek60122be2015-04-15 18:16:50 -0700160 private boolean mHideSensitiveForIntrinsicHeight;
felkachang08579552018-05-24 15:38:04 +0800161 private float mHeaderVisibleAmount = DEFAULT_HEADER_VISIBLE_AMOUNT;
Chris Wren51c75102013-07-16 20:49:17 -0400162
Selim Cinek1685e632014-04-08 02:27:49 +0200163 /**
164 * Is this notification expanded by the system. The expansion state can be overridden by the
165 * user expansion.
166 */
167 private boolean mIsSystemExpanded;
Jorim Jaggidce3c4c2014-04-29 23:12:24 +0200168
169 /**
Selim Cinek83bc7832015-10-22 13:26:54 -0700170 * Whether the notification is on the keyguard and the expansion is disabled.
Jorim Jaggidce3c4c2014-04-29 23:12:24 +0200171 */
Selim Cinek83bc7832015-10-22 13:26:54 -0700172 private boolean mOnKeyguard;
Jorim Jaggidce3c4c2014-04-29 23:12:24 +0200173
Mady Mellorb0a82462016-04-30 17:31:02 -0700174 private Animator mTranslateAnim;
Mady Mellor4b80b102016-01-22 08:03:58 -0800175 private ArrayList<View> mTranslateableViews;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200176 private NotificationContentView mPublicLayout;
177 private NotificationContentView mPrivateLayout;
Adrian Rooseb434ff2017-01-11 11:18:48 -0800178 private NotificationContentView[] mLayouts;
Selim Cinek4bb59342016-04-08 19:29:35 -0700179 private int mNotificationColor;
Chris Wren78403d72014-07-28 10:23:24 +0100180 private ExpansionLogger mLogger;
181 private String mLoggingKey;
Selim Cinek8d490d42015-04-10 00:05:50 -0700182 private NotificationGuts mGuts;
Selim Cinekda42d652015-12-04 15:51:16 -0800183 private NotificationData.Entry mEntry;
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200184 private StatusBarNotification mStatusBarNotification;
Mady Mellor3fd273e2016-03-15 21:08:14 -0700185 private String mAppName;
Selim Cinek1a521f32014-11-03 17:39:29 +0100186 private boolean mIsHeadsUp;
Selim Cinek98713a42015-09-21 15:47:20 +0200187 private boolean mLastChronometerRunning = true;
Selim Cinekb5605e52015-02-20 18:21:41 +0100188 private ViewStub mChildrenContainerStub;
189 private NotificationGroupManager mGroupManager;
Selim Cinekb5605e52015-02-20 18:21:41 +0100190 private boolean mChildrenExpanded;
Selim Cinek263398f2015-10-21 17:40:23 -0700191 private boolean mIsSummaryWithChildren;
Selim Cinekb5605e52015-02-20 18:21:41 +0100192 private NotificationChildrenContainer mChildrenContainer;
Mady Mellor95d743c2017-01-10 12:05:27 -0800193 private NotificationMenuRowPlugin mMenuRow;
Selim Cinekab29aeb2015-02-20 18:18:32 +0100194 private ViewStub mGutsStub;
Selim Cinekb5605e52015-02-20 18:21:41 +0100195 private boolean mIsSystemChildExpanded;
Selim Cinek684a4422015-04-15 16:18:39 -0700196 private boolean mIsPinned;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700197 private FalsingManager mFalsingManager;
Selim Cinek2627d722018-01-19 12:16:49 -0800198 private boolean mExpandAnimationRunning;
Selim Cinek5cf1d052017-06-01 17:36:46 -0700199 private AboveShelfChangedListener mAboveShelfChangedListener;
Selim Cinek31aada42015-12-18 17:51:15 -0800200 private HeadsUpManager mHeadsUpManager;
Selim Cinek8875de12018-03-22 10:14:32 -0700201 private Consumer<Boolean> mHeadsUpAnimatingAwayListener;
Selim Cinekc25989e2018-02-16 16:42:14 -0800202 private boolean mChildIsExpanding;
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200203
Jorim Jaggi5eb67c22015-08-19 19:50:49 -0700204 private boolean mJustClicked;
Selim Cinek5a175d92015-11-23 18:01:33 -0800205 private boolean mIconAnimationRunning;
Selim Cinek34d93b02015-10-22 12:30:38 -0700206 private boolean mShowNoBackground;
Selim Cinek388df6d2015-10-22 13:25:11 -0700207 private ExpandableNotificationRow mNotificationParent;
Selim Cinek570981d2015-12-01 11:37:01 -0800208 private OnExpandClickListener mOnExpandClickListener;
Julia Reynoldsb5867452018-02-28 16:31:35 -0500209 private View.OnClickListener mOnAppOpsClickListener;
Geoffrey Pitsch409db272017-08-28 14:51:52 +0000210
211 // Listener will be called when receiving a long click event.
212 // Use #setLongPressPosition to optionally assign positional data with the long press.
213 private LongPressListener mLongPressListener;
214
Mady Mellorb0a82462016-04-30 17:31:02 -0700215 private boolean mGroupExpansionChanging;
216
Anthony Chen6bf88a02017-04-10 14:41:44 -0700217 /**
Lucas Dupincecc7c22017-09-12 16:02:45 -0700218 * A supplier that returns true if keyguard is secure.
219 */
220 private BooleanSupplier mSecureStateProvider;
221
222 /**
Anthony Chen6bf88a02017-04-10 14:41:44 -0700223 * Whether or not a notification that is not part of a group of notifications can be manually
224 * expanded by the user.
225 */
226 private boolean mEnableNonGroupedNotificationExpand;
227
228 /**
229 * Whether or not to update the background of the header of the notification when its expanded.
230 * If {@code true}, the header background will disappear when expanded.
231 */
232 private boolean mShowGroupBackgroundWhenExpanded;
233
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800234 private OnClickListener mExpandClickListener = new OnClickListener() {
235 @Override
236 public void onClick(View v) {
Selim Cinek0b9cf462017-12-07 16:31:03 -0800237 if (!shouldShowPublic() && (!mIsLowPriority || isExpanded())
Selim Cinek414ad332017-02-24 19:06:12 -0800238 && mGroupManager.isSummaryOfGroup(mStatusBarNotification)) {
Mady Mellor1a5d8ea2016-06-09 10:42:42 -0700239 mGroupExpansionChanging = true;
Chris Wren698b1702016-05-23 11:16:32 -0400240 final boolean wasExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
241 boolean nowExpanded = mGroupManager.toggleGroupExpansion(mStatusBarNotification);
242 mOnExpandClickListener.onExpandClicked(mEntry, nowExpanded);
Chris Wren698b1702016-05-23 11:16:32 -0400243 MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTIFICATION_GROUP_EXPANDER,
244 nowExpanded);
Selim Cinek414ad332017-02-24 19:06:12 -0800245 onExpansionChanged(true /* userAction */, wasExpanded);
Anthony Chen6bf88a02017-04-10 14:41:44 -0700246 } else if (mEnableNonGroupedNotificationExpand) {
Selim Cineke9bad242016-06-15 11:46:37 -0700247 if (v.isAccessibilityFocused()) {
248 mPrivateLayout.setFocusOnVisibilityChange();
249 }
Selim Cinek31aada42015-12-18 17:51:15 -0800250 boolean nowExpanded;
251 if (isPinned()) {
252 nowExpanded = !mExpandedWhenPinned;
253 mExpandedWhenPinned = nowExpanded;
254 } else {
255 nowExpanded = !isExpanded();
256 setUserExpanded(nowExpanded);
257 }
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800258 notifyHeightChanged(true);
Selim Cinek31aada42015-12-18 17:51:15 -0800259 mOnExpandClickListener.onExpandClicked(mEntry, nowExpanded);
Chris Wren698b1702016-05-23 11:16:32 -0400260 MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTIFICATION_EXPANDER,
261 nowExpanded);
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800262 }
263 }
264 };
Selim Cinek1b2a05e2016-04-28 14:20:39 -0700265 private boolean mForceUnlocked;
Selim Cinek3f19f602016-05-02 18:01:56 -0700266 private boolean mDismissed;
267 private boolean mKeepInParent;
268 private boolean mRemoved;
Mady Mellorb0a82462016-04-30 17:31:02 -0700269 private static final Property<ExpandableNotificationRow, Float> TRANSLATE_CONTENT =
270 new FloatProperty<ExpandableNotificationRow>("translate") {
271 @Override
272 public void setValue(ExpandableNotificationRow object, float value) {
273 object.setTranslation(value);
274 }
275
276 @Override
277 public Float get(ExpandableNotificationRow object) {
278 return object.getTranslation();
279 }
Rohan Shah4ed1b2a2018-03-30 13:26:01 -0700280 };
Selim Cinekddf1b392016-05-27 16:33:10 -0700281 private OnClickListener mOnClickListener;
Selim Cinek73cf02a2016-06-17 13:08:00 -0700282 private boolean mHeadsupDisappearRunning;
Selim Cineke9bad242016-06-15 11:46:37 -0700283 private View mChildAfterViewWhenDismissed;
284 private View mGroupParentWhenDismissed;
285 private boolean mRefocusOnDismiss;
Selim Cinek2b549f42016-11-22 16:38:51 -0800286 private float mContentTransformationAmount;
Selim Cinek0242fbb2016-10-19 13:38:32 -0700287 private boolean mIconsVisible = true;
Selim Cinekd127d792016-11-01 19:11:41 -0700288 private boolean mAboveShelf;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800289 private boolean mShowAmbient;
Selim Cinek875a3a12016-11-18 17:52:16 -0800290 private boolean mIsLastChild;
Selim Cineke9079112016-12-14 14:41:01 -0800291 private Runnable mOnDismissRunnable;
Selim Cinek6743c0b2017-01-18 18:24:01 -0800292 private boolean mIsLowPriority;
Selim Cineka7ed2c12017-01-23 20:47:24 -0800293 private boolean mIsColorized;
Selim Cinek7d1009b2017-01-25 15:28:28 -0800294 private boolean mUseIncreasedCollapsedHeight;
Selim Cinek87ed69b2017-02-09 15:59:43 -0800295 private boolean mUseIncreasedHeadsUpHeight;
Selim Cinekef8c2252017-02-10 14:52:18 -0800296 private float mTranslationWhenRemoved;
297 private boolean mWasChildInGroupWhenRemoved;
Adrian Roos6f6e1592017-05-02 16:22:53 -0700298 private int mNotificationColorAmbient;
Selim Cinek2627d722018-01-19 12:16:49 -0800299 private NotificationViewState mNotificationViewState;
Mady Mellorb0a82462016-04-30 17:31:02 -0700300
Rohan Shah4ed1b2a2018-03-30 13:26:01 -0700301 private SystemNotificationAsyncTask mSystemNotificationAsyncTask =
302 new SystemNotificationAsyncTask();
303
304 /**
305 * Returns whether the given {@code statusBarNotification} is a system notification.
306 * <b>Note</b>, this should be run in the background thread if possible as it makes multiple IPC
307 * calls.
308 */
309 private static Boolean isSystemNotification(
310 Context context, StatusBarNotification statusBarNotification) {
311 PackageManager packageManager = StatusBar.getPackageManagerForUser(
312 context, statusBarNotification.getUser().getIdentifier());
313 Boolean isSystemNotification = null;
314
315 try {
316 PackageInfo packageInfo = packageManager.getPackageInfo(
317 statusBarNotification.getPackageName(), PackageManager.GET_SIGNATURES);
318
319 isSystemNotification =
320 com.android.settingslib.Utils.isSystemPackage(
321 context.getResources(), packageManager, packageInfo);
322 } catch (PackageManager.NameNotFoundException e) {
323 Log.e(TAG, "cacheIsSystemNotification: Could not find package info");
324 }
325 return isSystemNotification;
326 }
327
Mady Mellor43c2cd12016-12-12 21:05:13 -0800328 @Override
Mady Mellorb0a82462016-04-30 17:31:02 -0700329 public boolean isGroupExpansionChanging() {
330 if (isChildInGroup()) {
331 return mNotificationParent.isGroupExpansionChanging();
332 }
333 return mGroupExpansionChanging;
334 }
335
336 public void setGroupExpansionChanging(boolean changing) {
337 mGroupExpansionChanging = changing;
338 }
Jorim Jaggi5eb67c22015-08-19 19:50:49 -0700339
Adrian Roos599be342016-06-13 14:54:39 -0700340 @Override
341 public void setActualHeightAnimating(boolean animating) {
342 if (mPrivateLayout != null) {
343 mPrivateLayout.setContentHeightAnimating(animating);
344 }
345 }
346
Selim Cinek8d490d42015-04-10 00:05:50 -0700347 public NotificationContentView getPrivateLayout() {
348 return mPrivateLayout;
349 }
350
351 public NotificationContentView getPublicLayout() {
352 return mPublicLayout;
353 }
354
Selim Cinekcab4a602014-09-03 14:47:57 +0200355 public void setIconAnimationRunning(boolean running) {
Adrian Rooseb434ff2017-01-11 11:18:48 -0800356 for (NotificationContentView l : mLayouts) {
357 setIconAnimationRunning(running, l);
358 }
Selim Cinek5a175d92015-11-23 18:01:33 -0800359 if (mIsSummaryWithChildren) {
Mady Mellorb0a82462016-04-30 17:31:02 -0700360 setIconAnimationRunningForChild(running, mChildrenContainer.getHeaderView());
Selim Cinek414ad332017-02-24 19:06:12 -0800361 setIconAnimationRunningForChild(running, mChildrenContainer.getLowPriorityHeaderView());
Selim Cinek5a175d92015-11-23 18:01:33 -0800362 List<ExpandableNotificationRow> notificationChildren =
363 mChildrenContainer.getNotificationChildren();
364 for (int i = 0; i < notificationChildren.size(); i++) {
365 ExpandableNotificationRow child = notificationChildren.get(i);
366 child.setIconAnimationRunning(running);
367 }
368 }
369 mIconAnimationRunning = running;
Selim Cinekcab4a602014-09-03 14:47:57 +0200370 }
371
372 private void setIconAnimationRunning(boolean running, NotificationContentView layout) {
373 if (layout != null) {
374 View contractedChild = layout.getContractedChild();
375 View expandedChild = layout.getExpandedChild();
Selim Cinek8d490d42015-04-10 00:05:50 -0700376 View headsUpChild = layout.getHeadsUpChild();
Selim Cinekcab4a602014-09-03 14:47:57 +0200377 setIconAnimationRunningForChild(running, contractedChild);
378 setIconAnimationRunningForChild(running, expandedChild);
Selim Cinek8d490d42015-04-10 00:05:50 -0700379 setIconAnimationRunningForChild(running, headsUpChild);
Selim Cinekcab4a602014-09-03 14:47:57 +0200380 }
381 }
382
383 private void setIconAnimationRunningForChild(boolean running, View child) {
384 if (child != null) {
385 ImageView icon = (ImageView) child.findViewById(com.android.internal.R.id.icon);
386 setIconRunning(icon, running);
387 ImageView rightIcon = (ImageView) child.findViewById(
388 com.android.internal.R.id.right_icon);
389 setIconRunning(rightIcon, running);
390 }
391 }
392
393 private void setIconRunning(ImageView imageView, boolean running) {
394 if (imageView != null) {
395 Drawable drawable = imageView.getDrawable();
396 if (drawable instanceof AnimationDrawable) {
397 AnimationDrawable animationDrawable = (AnimationDrawable) drawable;
398 if (running) {
399 animationDrawable.start();
400 } else {
401 animationDrawable.stop();
402 }
403 } else if (drawable instanceof AnimatedVectorDrawable) {
404 AnimatedVectorDrawable animationDrawable = (AnimatedVectorDrawable) drawable;
405 if (running) {
406 animationDrawable.start();
407 } else {
408 animationDrawable.stop();
409 }
410 }
411 }
412 }
413
Selim Cinek5ba22542017-04-20 15:16:10 -0700414 public void updateNotification(NotificationData.Entry entry) {
Selim Cinekda42d652015-12-04 15:51:16 -0800415 mEntry = entry;
416 mStatusBarNotification = entry.notification;
Selim Cinek1a48bab2017-02-17 19:38:40 -0800417 mNotificationInflater.inflateNotificationViews();
Rohan Shah63411fc2018-03-28 19:05:52 -0700418
Rohan Shah4ed1b2a2018-03-30 13:26:01 -0700419 cacheIsSystemNotification();
Rohan Shah63411fc2018-03-28 19:05:52 -0700420 }
421
422 /**
Rohan Shah4ed1b2a2018-03-30 13:26:01 -0700423 * Caches whether or not this row contains a system notification. Note, this is only cached
424 * once per notification as the packageInfo can't technically change for a notification row.
Rohan Shah63411fc2018-03-28 19:05:52 -0700425 */
Rohan Shah4ed1b2a2018-03-30 13:26:01 -0700426 private void cacheIsSystemNotification() {
Julia Reynoldsaa96cf32018-04-17 09:09:04 -0400427 if (mEntry != null && mEntry.mIsSystemNotification == null) {
Rohan Shah4ed1b2a2018-03-30 13:26:01 -0700428 if (mSystemNotificationAsyncTask.getStatus() == AsyncTask.Status.PENDING) {
429 // Run async task once, only if it hasn't already been executed. Note this is
430 // executed in serial - no need to parallelize this small task.
431 mSystemNotificationAsyncTask.execute();
Rohan Shah63411fc2018-03-28 19:05:52 -0700432 }
433 }
434 }
435
436 /**
Rohan Shahca0447e2018-03-30 15:18:27 -0700437 * Returns whether this row is considered non-blockable (i.e. it's a non-blockable system notif
438 * or is in a whitelist).
Rohan Shah63411fc2018-03-28 19:05:52 -0700439 */
440 public boolean getIsNonblockable() {
Rohan Shah4ed1b2a2018-03-30 13:26:01 -0700441 boolean isNonblockable = Dependency.get(NotificationBlockingHelperManager.class)
Rohan Shah63411fc2018-03-28 19:05:52 -0700442 .isNonblockablePackage(mStatusBarNotification.getPackageName());
443
Rohan Shah4ed1b2a2018-03-30 13:26:01 -0700444 // If the SystemNotifAsyncTask hasn't finished running or retrieved a value, we'll try once
445 // again, but in-place on the main thread this time. This should rarely ever get called.
Julia Reynoldsaa96cf32018-04-17 09:09:04 -0400446 if (mEntry != null && mEntry.mIsSystemNotification == null) {
Rohan Shah4ed1b2a2018-03-30 13:26:01 -0700447 if (DEBUG) {
448 Log.d(TAG, "Retrieving isSystemNotification on main thread");
449 }
450 mSystemNotificationAsyncTask.cancel(true /* mayInterruptIfRunning */);
Julia Reynoldsaa96cf32018-04-17 09:09:04 -0400451 mEntry.mIsSystemNotification = isSystemNotification(mContext, mStatusBarNotification);
Rohan Shah4ed1b2a2018-03-30 13:26:01 -0700452 }
453
Julia Reynoldsaa96cf32018-04-17 09:09:04 -0400454 if (!isNonblockable && mEntry != null && mEntry.mIsSystemNotification != null) {
455 if (mEntry.mIsSystemNotification) {
Rohan Shah63411fc2018-03-28 19:05:52 -0700456 if (mEntry.channel != null
457 && !mEntry.channel.isBlockableSystem()) {
458 isNonblockable = true;
459 }
460 }
461 }
462 return isNonblockable;
Selim Cinekc478f902017-02-22 20:55:44 -0800463 }
464
Selim Cinek5ba22542017-04-20 15:16:10 -0700465 public void onNotificationUpdated() {
Adrian Rooseb434ff2017-01-11 11:18:48 -0800466 for (NotificationContentView l : mLayouts) {
Selim Cinekc478f902017-02-22 20:55:44 -0800467 l.onNotificationUpdated(mEntry);
Adrian Rooseb434ff2017-01-11 11:18:48 -0800468 }
Selim Cineka7ed2c12017-01-23 20:47:24 -0800469 mIsColorized = mStatusBarNotification.getNotification().isColorized();
Selim Cinek757d8792016-01-28 16:21:08 -0800470 mShowingPublicInitialized = false;
Selim Cinek4bb59342016-04-08 19:29:35 -0700471 updateNotificationColor();
Mady Mellor4c197602017-04-10 17:57:52 -0700472 if (mMenuRow != null) {
Mady Mellor4ab28202017-06-06 11:42:50 -0700473 mMenuRow.onNotificationUpdated(mStatusBarNotification);
Beverly3f56f052018-05-08 14:56:03 -0400474 mMenuRow.setAppName(mAppName);
Mady Mellor4c197602017-04-10 17:57:52 -0700475 }
Selim Cinek8fc93c92015-11-23 17:48:07 -0800476 if (mIsSummaryWithChildren) {
Selim Cinek414ad332017-02-24 19:06:12 -0800477 mChildrenContainer.recreateNotificationHeader(mExpandClickListener);
Selim Cinekc897bd32016-03-18 17:32:31 -0700478 mChildrenContainer.onNotificationUpdated();
Selim Cinek8fc93c92015-11-23 17:48:07 -0800479 }
Selim Cinek5a175d92015-11-23 18:01:33 -0800480 if (mIconAnimationRunning) {
481 setIconAnimationRunning(true);
482 }
Selim Cinekea4bef72015-12-02 15:51:10 -0800483 if (mNotificationParent != null) {
484 mNotificationParent.updateChildrenHeaderAppearance();
485 }
Selim Cinek263398f2015-10-21 17:40:23 -0700486 onChildrenCountChanged();
Selim Cinek624c02db2015-12-14 21:00:02 -0800487 // The public layouts expand button is always visible
488 mPublicLayout.updateExpandButtons(true);
Selim Cinekda42d652015-12-04 15:51:16 -0800489 updateLimits();
Selim Cinek0242fbb2016-10-19 13:38:32 -0700490 updateIconVisibilities();
Selim Cinek6743c0b2017-01-18 18:24:01 -0800491 updateShelfIconColor();
Selim Cinekfe24fb72018-02-13 14:34:55 -0800492 updateRippleAllowed();
Selim Cinek6743c0b2017-01-18 18:24:01 -0800493 }
494
Lucas Dupinb6ed63b2017-05-30 16:17:42 -0700495 @VisibleForTesting
496 void updateShelfIconColor() {
Selim Cinek6743c0b2017-01-18 18:24:01 -0800497 StatusBarIconView expandedIcon = mEntry.expandedIcon;
498 boolean isPreL = Boolean.TRUE.equals(expandedIcon.getTag(R.id.icon_is_pre_L));
499 boolean colorize = !isPreL || NotificationUtils.isGrayscale(expandedIcon,
500 NotificationColorUtil.getInstance(mContext));
Selim Cinek875ba9b2017-02-13 16:20:17 -0800501 int color = StatusBarIconView.NO_COLOR;
Selim Cinek6743c0b2017-01-18 18:24:01 -0800502 if (colorize) {
Lucas Dupinb6ed63b2017-05-30 16:17:42 -0700503 NotificationHeaderView header = getVisibleNotificationHeader();
504 if (header != null) {
505 color = header.getOriginalIconColor();
506 } else {
507 color = mEntry.getContrastedColor(mContext, mIsLowPriority && !isExpanded(),
508 getBackgroundColorWithoutTint());
509 }
Selim Cinek6743c0b2017-01-18 18:24:01 -0800510 }
Selim Cinek875ba9b2017-02-13 16:20:17 -0800511 expandedIcon.setStaticDrawableColor(color);
Selim Cinekda42d652015-12-04 15:51:16 -0800512 }
513
Selim Cinek5cf1d052017-06-01 17:36:46 -0700514 public void setAboveShelfChangedListener(AboveShelfChangedListener aboveShelfChangedListener) {
515 mAboveShelfChangedListener = aboveShelfChangedListener;
516 }
517
Lucas Dupincecc7c22017-09-12 16:02:45 -0700518 /**
519 * Sets a supplier that can determine whether the keyguard is secure or not.
520 * @param secureStateProvider A function that returns true if keyguard is secure.
521 */
522 public void setSecureStateProvider(BooleanSupplier secureStateProvider) {
523 mSecureStateProvider = secureStateProvider;
524 }
525
Selim Cinek4705f292017-04-24 22:18:48 -0700526 @Override
527 public boolean isDimmable() {
528 if (!getShowingLayout().isDimmable()) {
529 return false;
530 }
531 return super.isDimmable();
532 }
533
Selim Cinekda42d652015-12-04 15:51:16 -0800534 private void updateLimits() {
Adrian Rooseb434ff2017-01-11 11:18:48 -0800535 for (NotificationContentView l : mLayouts) {
536 updateLimitsForView(l);
537 }
Selim Cineka1744872016-03-11 15:36:06 -0800538 }
539
540 private void updateLimitsForView(NotificationContentView layout) {
541 boolean customView = layout.getContractedChild().getId()
Selim Cinekda42d652015-12-04 15:51:16 -0800542 != com.android.internal.R.id.status_bar_latest_event_content;
543 boolean beforeN = mEntry.targetSdk < Build.VERSION_CODES.N;
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800544 boolean beforeP = mEntry.targetSdk < Build.VERSION_CODES.P;
Selim Cinek7d1009b2017-01-25 15:28:28 -0800545 int minHeight;
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800546 if (customView && beforeP && !mIsSummaryWithChildren) {
547 minHeight = beforeN ? mNotificationMinHeightLegacy : mNotificationMinHeightBeforeP;
Selim Cinek7d1009b2017-01-25 15:28:28 -0800548 } else if (mUseIncreasedCollapsedHeight && layout == mPrivateLayout) {
549 minHeight = mNotificationMinHeightLarge;
550 } else {
551 minHeight = mNotificationMinHeight;
552 }
Selim Cineka1744872016-03-11 15:36:06 -0800553 boolean headsUpCustom = layout.getHeadsUpChild() != null &&
554 layout.getHeadsUpChild().getId()
555 != com.android.internal.R.id.status_bar_latest_event_content;
Selim Cinek87ed69b2017-02-09 15:59:43 -0800556 int headsUpheight;
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800557 if (headsUpCustom && beforeP) {
558 headsUpheight = beforeN ? mMaxHeadsUpHeightLegacy : mMaxHeadsUpHeightBeforeP;
Selim Cinek87ed69b2017-02-09 15:59:43 -0800559 } else if (mUseIncreasedHeadsUpHeight && layout == mPrivateLayout) {
560 headsUpheight = mMaxHeadsUpHeightIncreased;
561 } else {
562 headsUpheight = mMaxHeadsUpHeight;
563 }
Selim Cineke62255c2017-09-28 18:23:23 -0700564 NotificationViewWrapper headsUpWrapper = layout.getVisibleWrapper(
565 NotificationContentView.VISIBLE_TYPE_HEADSUP);
566 if (headsUpWrapper != null) {
567 headsUpheight = Math.max(headsUpheight, headsUpWrapper.getMinLayoutHeight());
568 }
Adrian Roos0aac04f2016-12-08 15:59:29 -0800569 layout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight,
570 mNotificationAmbientHeight);
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200571 }
572
573 public StatusBarNotification getStatusBarNotification() {
574 return mStatusBarNotification;
575 }
576
Selim Cinek281c2022016-10-13 19:14:43 -0700577 public NotificationData.Entry getEntry() {
578 return mEntry;
579 }
580
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700581 public boolean isHeadsUp() {
582 return mIsHeadsUp;
583 }
584
Selim Cinek1a521f32014-11-03 17:39:29 +0100585 public void setHeadsUp(boolean isHeadsUp) {
Selim Cinek5cf1d052017-06-01 17:36:46 -0700586 boolean wasAboveShelf = isAboveShelf();
Selim Cinekc80fdb12015-04-13 15:09:08 -0700587 int intrinsicBefore = getIntrinsicHeight();
Selim Cinek1a521f32014-11-03 17:39:29 +0100588 mIsHeadsUp = isHeadsUp;
Selim Cinek8d490d42015-04-10 00:05:50 -0700589 mPrivateLayout.setHeadsUp(isHeadsUp);
Selim Cinekb41b2f62016-04-26 14:03:29 -0700590 if (mIsSummaryWithChildren) {
591 // The overflow might change since we allow more lines as HUN.
592 mChildrenContainer.updateGroupOverflow();
593 }
Selim Cinekc80fdb12015-04-13 15:09:08 -0700594 if (intrinsicBefore != getIntrinsicHeight()) {
595 notifyHeightChanged(false /* needsAnimation */);
596 }
Selim Cinekd127d792016-11-01 19:11:41 -0700597 if (isHeadsUp) {
Selim Cinek9b9d6e12017-11-30 12:29:47 +0100598 mMustStayOnScreen = true;
Selim Cinekd127d792016-11-01 19:11:41 -0700599 setAboveShelf(true);
Selim Cinek5cf1d052017-06-01 17:36:46 -0700600 } else if (isAboveShelf() != wasAboveShelf) {
601 mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
Selim Cinekd127d792016-11-01 19:11:41 -0700602 }
Selim Cinek1a521f32014-11-03 17:39:29 +0100603 }
604
Selim Cinekb5605e52015-02-20 18:21:41 +0100605 public void setGroupManager(NotificationGroupManager groupManager) {
606 mGroupManager = groupManager;
Selim Cinek83bc7832015-10-22 13:26:54 -0700607 mPrivateLayout.setGroupManager(groupManager);
Selim Cinekb5605e52015-02-20 18:21:41 +0100608 }
609
Adrian Roosb88b1a12015-12-09 18:51:05 -0800610 public void setRemoteInputController(RemoteInputController r) {
611 mPrivateLayout.setRemoteInputController(r);
612 }
613
Mady Mellor3fd273e2016-03-15 21:08:14 -0700614 public void setAppName(String appName) {
615 mAppName = appName;
Mady Mellor95d743c2017-01-10 12:05:27 -0800616 if (mMenuRow != null && mMenuRow.getMenuView() != null) {
Mady Mellor761cde12017-01-10 11:36:39 -0800617 mMenuRow.setAppName(mAppName);
Mady Mellor3fd273e2016-03-15 21:08:14 -0700618 }
619 }
620
Selim Cinekb5605e52015-02-20 18:21:41 +0100621 public void addChildNotification(ExpandableNotificationRow row) {
622 addChildNotification(row, -1);
623 }
624
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800625 /**
626 * Set the how much the header should be visible. A value of 0 will make the header fully gone
627 * and a value of 1 will make the notification look just like normal.
628 * This is being used for heads up notifications, when they are pinned to the top of the screen
629 * and the header content is extracted to the statusbar.
630 *
631 * @param headerVisibleAmount the amount the header should be visible.
632 */
633 public void setHeaderVisibleAmount(float headerVisibleAmount) {
634 if (mHeaderVisibleAmount != headerVisibleAmount) {
635 mHeaderVisibleAmount = headerVisibleAmount;
636 mPrivateLayout.setHeaderVisibleAmount(headerVisibleAmount);
637 if (mChildrenContainer != null) {
638 mChildrenContainer.setHeaderVisibleAmount(headerVisibleAmount);
639 }
640 notifyHeightChanged(false /* needsAnimation */);
641 }
642 }
643
Selim Cinek99e9adf2018-03-15 09:17:47 -0700644 @Override
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800645 public float getHeaderVisibleAmount() {
646 return mHeaderVisibleAmount;
647 }
648
Selim Cinek9b9d6e12017-11-30 12:29:47 +0100649 @Override
650 public void setHeadsUpIsVisible() {
651 super.setHeadsUpIsVisible();
652 mMustStayOnScreen = false;
653 }
654
Selim Cinekb5605e52015-02-20 18:21:41 +0100655 /**
656 * Add a child notification to this view.
657 *
658 * @param row the row to add
659 * @param childIndex the index to add it at, if -1 it will be added at the end
660 */
661 public void addChildNotification(ExpandableNotificationRow row, int childIndex) {
662 if (mChildrenContainer == null) {
663 mChildrenContainerStub.inflate();
664 }
665 mChildrenContainer.addNotification(row, childIndex);
Selim Cinek263398f2015-10-21 17:40:23 -0700666 onChildrenCountChanged();
667 row.setIsChildInGroup(true, this);
Selim Cinekb5605e52015-02-20 18:21:41 +0100668 }
669
670 public void removeChildNotification(ExpandableNotificationRow row) {
671 if (mChildrenContainer != null) {
672 mChildrenContainer.removeNotification(row);
673 }
Selim Cinek263398f2015-10-21 17:40:23 -0700674 onChildrenCountChanged();
675 row.setIsChildInGroup(false, null);
Selim Cinek2871bef2017-11-22 08:40:00 -0800676 row.setBottomRoundness(0.0f, false /* animate */);
Selim Cinek263398f2015-10-21 17:40:23 -0700677 }
678
Mady Mellor43c2cd12016-12-12 21:05:13 -0800679 @Override
Selim Cinek263398f2015-10-21 17:40:23 -0700680 public boolean isChildInGroup() {
Selim Cineka6c6bfb2015-10-29 16:27:08 -0700681 return mNotificationParent != null;
Selim Cinek263398f2015-10-21 17:40:23 -0700682 }
683
Rohan Shah524cf7b2018-03-15 14:40:02 -0700684 /**
685 * @return whether this notification is the only child in the group summary
686 */
687 public boolean isOnlyChildInGroup() {
688 return mGroupManager.isOnlyChildInGroup(getStatusBarNotification());
689 }
690
Selim Cinek388df6d2015-10-22 13:25:11 -0700691 public ExpandableNotificationRow getNotificationParent() {
692 return mNotificationParent;
693 }
694
Selim Cinek263398f2015-10-21 17:40:23 -0700695 /**
696 * @param isChildInGroup Is this notification now in a group
697 * @param parent the new parent notification
698 */
Selim Cinekc25989e2018-02-16 16:42:14 -0800699 public void setIsChildInGroup(boolean isChildInGroup, ExpandableNotificationRow parent) {
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500700 boolean childInGroup = StatusBar.ENABLE_CHILD_NOTIFICATIONS && isChildInGroup;
Selim Cinekc25989e2018-02-16 16:42:14 -0800701 if (mExpandAnimationRunning && !isChildInGroup && mNotificationParent != null) {
702 mNotificationParent.setChildIsExpanding(false);
703 mNotificationParent.setExtraWidthForClipping(0.0f);
704 mNotificationParent.setMinimumHeightForClipping(0);
705 }
Selim Cineka6c6bfb2015-10-29 16:27:08 -0700706 mNotificationParent = childInGroup ? parent : null;
707 mPrivateLayout.setIsChildInGroup(childInGroup);
Selim Cinek5ba22542017-04-20 15:16:10 -0700708 mNotificationInflater.setIsChildInGroup(childInGroup);
Mady Mellorc7d65b42016-05-04 11:44:57 -0400709 resetBackgroundAlpha();
Mady Mellorb0a82462016-04-30 17:31:02 -0700710 updateBackgroundForGroupState();
Selim Cinekddf1b392016-05-27 16:33:10 -0700711 updateClickAndFocus();
Mady Mellorb0a82462016-04-30 17:31:02 -0700712 if (mNotificationParent != null) {
Selim Cinek9ce32852017-02-15 16:21:10 -0800713 setOverrideTintColor(NO_COLOR, 0.0f);
Selim Cinekeccf4942018-05-30 09:55:36 -0700714 // Let's reset the distance to top roundness, as this isn't applied to group children
715 setDistanceToTopRoundness(NO_ROUNDNESS);
Mady Mellorb0a82462016-04-30 17:31:02 -0700716 mNotificationParent.updateBackgroundForGroupState();
717 }
Selim Cinekdb167372016-11-17 15:41:17 -0800718 updateIconVisibilities();
Selim Cinek2871bef2017-11-22 08:40:00 -0800719 updateBackgroundClipping();
Selim Cinek34d93b02015-10-22 12:30:38 -0700720 }
721
722 @Override
Selim Cinek72109472016-01-15 16:33:22 -0800723 public boolean onTouchEvent(MotionEvent event) {
724 if (event.getActionMasked() != MotionEvent.ACTION_DOWN
725 || !isChildInGroup() || isGroupExpanded()) {
726 return super.onTouchEvent(event);
727 } else {
728 return false;
729 }
730 }
731
732 @Override
Mady Mellorf0625802016-02-11 18:03:48 -0800733 protected boolean handleSlideBack() {
Mady Mellor95d743c2017-01-10 12:05:27 -0800734 if (mMenuRow != null && mMenuRow.isMenuVisible()) {
Mady Mellorf0625802016-02-11 18:03:48 -0800735 animateTranslateNotification(0 /* targetLeft */);
736 return true;
737 }
738 return false;
739 }
740
741 @Override
Selim Cinek34d93b02015-10-22 12:30:38 -0700742 protected boolean shouldHideBackground() {
743 return super.shouldHideBackground() || mShowNoBackground;
Selim Cinek263398f2015-10-21 17:40:23 -0700744 }
745
746 @Override
747 public boolean isSummaryWithChildren() {
748 return mIsSummaryWithChildren;
Selim Cinekb5605e52015-02-20 18:21:41 +0100749 }
750
751 @Override
752 public boolean areChildrenExpanded() {
753 return mChildrenExpanded;
754 }
755
756 public List<ExpandableNotificationRow> getNotificationChildren() {
757 return mChildrenContainer == null ? null : mChildrenContainer.getNotificationChildren();
758 }
759
Selim Cinekeef84282015-10-30 16:28:00 -0700760 public int getNumberOfNotificationChildren() {
761 if (mChildrenContainer == null) {
762 return 0;
763 }
764 return mChildrenContainer.getNotificationChildren().size();
765 }
766
Selim Cinekb5605e52015-02-20 18:21:41 +0100767 /**
768 * Apply the order given in the list to the children.
769 *
770 * @param childOrder the new list order
Selim Cineka7d4f822016-12-06 14:34:47 -0800771 * @param visualStabilityManager
772 * @param callback the callback to invoked in case it is not allowed
Selim Cinekb5605e52015-02-20 18:21:41 +0100773 * @return whether the list order has changed
774 */
Selim Cineka7d4f822016-12-06 14:34:47 -0800775 public boolean applyChildOrder(List<ExpandableNotificationRow> childOrder,
776 VisualStabilityManager visualStabilityManager,
777 VisualStabilityManager.Callback callback) {
778 return mChildrenContainer != null && mChildrenContainer.applyChildOrder(childOrder,
779 visualStabilityManager, callback);
Selim Cinekb5605e52015-02-20 18:21:41 +0100780 }
781
Selim Cinekc25989e2018-02-16 16:42:14 -0800782 public void getChildrenStates(StackScrollState resultState,
783 AmbientState ambientState) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700784 if (mIsSummaryWithChildren) {
Selim Cinekbbcebde2016-11-09 18:28:20 -0800785 ExpandableViewState parentState = resultState.getViewStateForView(this);
Selim Cinekc25989e2018-02-16 16:42:14 -0800786 mChildrenContainer.getState(resultState, parentState, ambientState);
Selim Cinekb5605e52015-02-20 18:21:41 +0100787 }
788 }
789
790 public void applyChildrenState(StackScrollState state) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700791 if (mIsSummaryWithChildren) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100792 mChildrenContainer.applyState(state);
793 }
794 }
795
796 public void prepareExpansionChanged(StackScrollState state) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700797 if (mIsSummaryWithChildren) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100798 mChildrenContainer.prepareExpansionChanged(state);
799 }
800 }
801
Selim Cinek0cfbef42016-11-09 19:06:36 -0800802 public void startChildAnimation(StackScrollState finalState, AnimationProperties properties) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700803 if (mIsSummaryWithChildren) {
Selim Cinek0cfbef42016-11-09 19:06:36 -0800804 mChildrenContainer.startAnimationToState(finalState, properties);
Selim Cinekb5605e52015-02-20 18:21:41 +0100805 }
806 }
807
808 public ExpandableNotificationRow getViewAtPosition(float y) {
Selim Cinek43d30f02016-03-04 10:51:32 -0800809 if (!mIsSummaryWithChildren || !mChildrenExpanded) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100810 return this;
811 } else {
812 ExpandableNotificationRow view = mChildrenContainer.getViewAtPosition(y);
813 return view == null ? this : view;
814 }
815 }
816
Selim Cinekab29aeb2015-02-20 18:18:32 +0100817 public NotificationGuts getGuts() {
818 return mGuts;
819 }
820
Selim Cinek684a4422015-04-15 16:18:39 -0700821 /**
822 * Set this notification to be pinned to the top if {@link #isHeadsUp()} is true. By doing this
823 * the notification will be rendered on top of the screen.
824 *
825 * @param pinned whether it is pinned
826 */
827 public void setPinned(boolean pinned) {
Selim Cinekdef35a82016-05-03 15:52:51 -0700828 int intrinsicHeight = getIntrinsicHeight();
Selim Cinek5cf1d052017-06-01 17:36:46 -0700829 boolean wasAboveShelf = isAboveShelf();
Selim Cinek684a4422015-04-15 16:18:39 -0700830 mIsPinned = pinned;
Selim Cinekdef35a82016-05-03 15:52:51 -0700831 if (intrinsicHeight != getIntrinsicHeight()) {
Selim Cinekbb42b7d2016-08-10 13:02:38 -0700832 notifyHeightChanged(false /* needsAnimation */);
Selim Cinekdef35a82016-05-03 15:52:51 -0700833 }
Selim Cinek31aada42015-12-18 17:51:15 -0800834 if (pinned) {
835 setIconAnimationRunning(true);
836 mExpandedWhenPinned = false;
837 } else if (mExpandedWhenPinned) {
838 setUserExpanded(true);
839 }
Selim Cinek98713a42015-09-21 15:47:20 +0200840 setChronometerRunning(mLastChronometerRunning);
Selim Cinek5cf1d052017-06-01 17:36:46 -0700841 if (isAboveShelf() != wasAboveShelf) {
842 mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
843 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700844 }
845
Selim Cinek29aab962018-02-27 17:05:45 -0800846 @Override
Selim Cinek684a4422015-04-15 16:18:39 -0700847 public boolean isPinned() {
848 return mIsPinned;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700849 }
850
Selim Cinekd127d792016-11-01 19:11:41 -0700851 @Override
852 public int getPinnedHeadsUpHeight() {
853 return getPinnedHeadsUpHeight(true /* atLeastMinHeight */);
854 }
855
Selim Cinek31aada42015-12-18 17:51:15 -0800856 /**
857 * @param atLeastMinHeight should the value returned be at least the minimum height.
858 * Used to avoid cyclic calls
859 * @return the height of the heads up notification when pinned
860 */
Selim Cinekd127d792016-11-01 19:11:41 -0700861 private int getPinnedHeadsUpHeight(boolean atLeastMinHeight) {
Selim Cinek77019c72015-12-09 10:18:02 -0800862 if (mIsSummaryWithChildren) {
863 return mChildrenContainer.getIntrinsicHeight();
864 }
Selim Cinek31aada42015-12-18 17:51:15 -0800865 if(mExpandedWhenPinned) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800866 return Math.max(getMaxExpandHeight(), getHeadsUpHeight());
Selim Cinek31aada42015-12-18 17:51:15 -0800867 } else if (atLeastMinHeight) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800868 return Math.max(getCollapsedHeight(), getHeadsUpHeight());
Selim Cinek31aada42015-12-18 17:51:15 -0800869 } else {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800870 return getHeadsUpHeight();
Selim Cinek31aada42015-12-18 17:51:15 -0800871 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700872 }
873
Jorim Jaggi5eb67c22015-08-19 19:50:49 -0700874 /**
875 * Mark whether this notification was just clicked, i.e. the user has just clicked this
876 * notification in this frame.
877 */
878 public void setJustClicked(boolean justClicked) {
879 mJustClicked = justClicked;
880 }
881
882 /**
883 * @return true if this notification has been clicked in this frame, false otherwise
884 */
885 public boolean wasJustClicked() {
886 return mJustClicked;
887 }
888
Selim Cinek98713a42015-09-21 15:47:20 +0200889 public void setChronometerRunning(boolean running) {
890 mLastChronometerRunning = running;
891 setChronometerRunning(running, mPrivateLayout);
892 setChronometerRunning(running, mPublicLayout);
893 if (mChildrenContainer != null) {
894 List<ExpandableNotificationRow> notificationChildren =
895 mChildrenContainer.getNotificationChildren();
896 for (int i = 0; i < notificationChildren.size(); i++) {
897 ExpandableNotificationRow child = notificationChildren.get(i);
898 child.setChronometerRunning(running);
899 }
900 }
901 }
902
903 private void setChronometerRunning(boolean running, NotificationContentView layout) {
904 if (layout != null) {
905 running = running || isPinned();
906 View contractedChild = layout.getContractedChild();
907 View expandedChild = layout.getExpandedChild();
908 View headsUpChild = layout.getHeadsUpChild();
909 setChronometerRunningForChild(running, contractedChild);
910 setChronometerRunningForChild(running, expandedChild);
911 setChronometerRunningForChild(running, headsUpChild);
912 }
913 }
914
915 private void setChronometerRunningForChild(boolean running, View child) {
916 if (child != null) {
917 View chronometer = child.findViewById(com.android.internal.R.id.chronometer);
918 if (chronometer instanceof Chronometer) {
919 ((Chronometer) chronometer).setStarted(running);
920 }
921 }
922 }
923
Selim Cinekea4bef72015-12-02 15:51:10 -0800924 public NotificationHeaderView getNotificationHeader() {
Mady Mellorb0a82462016-04-30 17:31:02 -0700925 if (mIsSummaryWithChildren) {
926 return mChildrenContainer.getHeaderView();
Selim Cinek8d6440d2015-10-22 13:00:05 -0700927 }
Selim Cinekea4bef72015-12-02 15:51:10 -0800928 return mPrivateLayout.getNotificationHeader();
Selim Cinek8d6440d2015-10-22 13:00:05 -0700929 }
930
Selim Cinek414ad332017-02-24 19:06:12 -0800931 /**
932 * @return the currently visible notification header. This can be different from
933 * {@link #getNotificationHeader()} in case it is a low-priority group.
934 */
935 public NotificationHeaderView getVisibleNotificationHeader() {
Selim Cinek0b9cf462017-12-07 16:31:03 -0800936 if (mIsSummaryWithChildren && !shouldShowPublic()) {
Selim Cinek414ad332017-02-24 19:06:12 -0800937 return mChildrenContainer.getVisibleHeader();
Selim Cinek34eda5e2016-02-18 17:10:43 -0800938 }
939 return getShowingLayout().getVisibleNotificationHeader();
940 }
941
Selim Cinekb26afa32017-06-29 10:28:17 +0200942
943 /**
944 * @return the contracted notification header. This can be different from
945 * {@link #getNotificationHeader()} and also {@link #getVisibleNotificationHeader()} and only
946 * returns the contracted version.
947 */
948 public NotificationHeaderView getContractedNotificationHeader() {
949 if (mIsSummaryWithChildren) {
950 return mChildrenContainer.getHeaderView();
951 }
952 return mPrivateLayout.getContractedNotificationHeader();
953 }
954
Selim Cinek570981d2015-12-01 11:37:01 -0800955 public void setOnExpandClickListener(OnExpandClickListener onExpandClickListener) {
956 mOnExpandClickListener = onExpandClickListener;
957 }
958
Geoffrey Pitsch409db272017-08-28 14:51:52 +0000959 public void setLongPressListener(LongPressListener longPressListener) {
960 mLongPressListener = longPressListener;
961 }
962
Selim Cinekddf1b392016-05-27 16:33:10 -0700963 @Override
964 public void setOnClickListener(@Nullable OnClickListener l) {
965 super.setOnClickListener(l);
966 mOnClickListener = l;
967 updateClickAndFocus();
968 }
969
970 private void updateClickAndFocus() {
971 boolean normalChild = !isChildInGroup() || isGroupExpanded();
972 boolean clickable = mOnClickListener != null && normalChild;
973 if (isFocusable() != normalChild) {
974 setFocusable(normalChild);
975 }
976 if (isClickable() != clickable) {
977 setClickable(clickable);
978 }
979 }
980
Selim Cinek31aada42015-12-18 17:51:15 -0800981 public void setHeadsUpManager(HeadsUpManager headsUpManager) {
982 mHeadsUpManager = headsUpManager;
983 }
984
Mady Mellor87d79452017-01-10 11:52:52 -0800985 public void setGutsView(MenuItem item) {
Mady Mellor95d743c2017-01-10 12:05:27 -0800986 if (mGuts != null && item.getGutsView() instanceof GutsContent) {
987 ((GutsContent) item.getGutsView()).setGutsParent(mGuts);
988 mGuts.setGutsContent((GutsContent) item.getGutsView());
Mady Mellor87d79452017-01-10 11:52:52 -0800989 }
990 }
991
Mady Mellor95d743c2017-01-10 12:05:27 -0800992 @Override
993 protected void onAttachedToWindow() {
994 super.onAttachedToWindow();
995 Dependency.get(PluginManager.class).addPluginListener(this,
996 NotificationMenuRowPlugin.class, false /* Allow multiple */);
997 }
998
999 @Override
1000 protected void onDetachedFromWindow() {
1001 super.onDetachedFromWindow();
1002 Dependency.get(PluginManager.class).removePluginListener(this);
1003 }
1004
1005 @Override
1006 public void onPluginConnected(NotificationMenuRowPlugin plugin, Context pluginContext) {
1007 boolean existed = mMenuRow.getMenuView() != null;
1008 if (existed) {
1009 removeView(mMenuRow.getMenuView());
1010 }
1011 mMenuRow = plugin;
1012 if (mMenuRow.useDefaultMenuItems()) {
Mady Mellor4c197602017-04-10 17:57:52 -07001013 ArrayList<MenuItem> items = new ArrayList<>();
1014 items.add(NotificationMenuRow.createInfoItem(mContext));
1015 items.add(NotificationMenuRow.createSnoozeItem(mContext));
Julia Reynoldsb5867452018-02-28 16:31:35 -05001016 items.add(NotificationMenuRow.createAppOpsItem(mContext));
Mady Mellor4c197602017-04-10 17:57:52 -07001017 mMenuRow.setMenuItems(items);
Mady Mellor95d743c2017-01-10 12:05:27 -08001018 }
1019 if (existed) {
1020 createMenu();
1021 }
1022 }
1023
1024 @Override
1025 public void onPluginDisconnected(NotificationMenuRowPlugin plugin) {
1026 boolean existed = mMenuRow.getMenuView() != null;
1027 mMenuRow = new NotificationMenuRow(mContext); // Back to default
1028 if (existed) {
1029 createMenu();
1030 }
1031 }
1032
1033 public NotificationMenuRowPlugin createMenu() {
1034 if (mMenuRow.getMenuView() == null) {
Mady Mellor4ab28202017-06-06 11:42:50 -07001035 mMenuRow.createMenu(this, mStatusBarNotification);
Mady Mellor95d743c2017-01-10 12:05:27 -08001036 mMenuRow.setAppName(mAppName);
1037 FrameLayout.LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT,
1038 LayoutParams.MATCH_PARENT);
1039 addView(mMenuRow.getMenuView(), MENU_VIEW_INDEX, lp);
1040 }
1041 return mMenuRow;
1042 }
1043
Mady Mellor95d743c2017-01-10 12:05:27 -08001044 public NotificationMenuRowPlugin getProvider() {
1045 return mMenuRow;
1046 }
1047
Anthony Chen9fe1ee72017-04-07 13:53:37 -07001048 @Override
Selim Cinek1a48bab2017-02-17 19:38:40 -08001049 public void onDensityOrFontScaleChanged() {
Anthony Chen9fe1ee72017-04-07 13:53:37 -07001050 super.onDensityOrFontScaleChanged();
Selim Cinek01af3342016-02-09 19:25:31 -08001051 initDimens();
Anthony Chenad4d1582017-04-10 16:07:58 -07001052 initBackground();
Selim Cinek817abe72017-05-24 11:08:55 -07001053 // Let's update our childrencontainer. This is intentionally not guarded with
1054 // mIsSummaryWithChildren since we might have had children but not anymore.
1055 if (mChildrenContainer != null) {
1056 mChildrenContainer.reInflateViews(mExpandClickListener, mEntry.notification);
Selim Cinek01af3342016-02-09 19:25:31 -08001057 }
1058 if (mGuts != null) {
1059 View oldGuts = mGuts;
1060 int index = indexOfChild(oldGuts);
1061 removeView(oldGuts);
1062 mGuts = (NotificationGuts) LayoutInflater.from(mContext).inflate(
1063 R.layout.notification_guts, this, false);
1064 mGuts.setVisibility(oldGuts.getVisibility());
1065 addView(mGuts, index);
1066 }
Mady Mellor95d743c2017-01-10 12:05:27 -08001067 View oldMenu = mMenuRow.getMenuView();
1068 if (oldMenu != null) {
Mady Mellor761cde12017-01-10 11:36:39 -08001069 int menuIndex = indexOfChild(oldMenu);
1070 removeView(oldMenu);
Mady Mellor4ab28202017-06-06 11:42:50 -07001071 mMenuRow.createMenu(ExpandableNotificationRow.this, mStatusBarNotification);
Mady Mellor761cde12017-01-10 11:36:39 -08001072 mMenuRow.setAppName(mAppName);
Mady Mellor95d743c2017-01-10 12:05:27 -08001073 addView(mMenuRow.getMenuView(), menuIndex);
Mady Mellor4b80b102016-01-22 08:03:58 -08001074 }
Adrian Rooseb434ff2017-01-11 11:18:48 -08001075 for (NotificationContentView l : mLayouts) {
Selim Cinek9b49f6d2017-11-13 18:19:35 -08001076 l.initView();
Adrian Rooseb434ff2017-01-11 11:18:48 -08001077 l.reInflateViews();
1078 }
Selim Cinek1a48bab2017-02-17 19:38:40 -08001079 mNotificationInflater.onDensityOrFontScaleChanged();
1080 onNotificationUpdated();
Selim Cinek01af3342016-02-09 19:25:31 -08001081 }
1082
Mady Mellor9d03a522017-04-04 18:45:30 -07001083 @Override
1084 public void onConfigurationChanged(Configuration newConfig) {
1085 if (mMenuRow.getMenuView() != null) {
1086 mMenuRow.onConfigurationChanged();
1087 }
1088 }
1089
Selim Cinekc3179332016-03-04 14:44:56 -08001090 public void setContentBackground(int customBackgroundColor, boolean animate,
1091 NotificationContentView notificationContentView) {
1092 if (getShowingLayout() == notificationContentView) {
1093 setTintColor(customBackgroundColor, animate);
1094 }
1095 }
1096
Kenny Guy14d035c2018-05-02 19:10:36 +01001097 @Override
1098 protected void setBackgroundTintColor(int color) {
1099 super.setBackgroundTintColor(color);
1100 NotificationContentView view = getShowingLayout();
1101 if (view != null) {
1102 view.setBackgroundTintColor(color);
1103 }
1104 }
1105
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001106 public void closeRemoteInput() {
Adrian Rooseb434ff2017-01-11 11:18:48 -08001107 for (NotificationContentView l : mLayouts) {
1108 l.closeRemoteInput();
1109 }
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001110 }
1111
Selim Cinekc897bd32016-03-18 17:32:31 -07001112 /**
1113 * Set by how much the single line view should be indented.
1114 */
1115 public void setSingleLineWidthIndention(int indention) {
1116 mPrivateLayout.setSingleLineWidthIndention(indention);
1117 }
1118
1119 public int getNotificationColor() {
Selim Cinek4bb59342016-04-08 19:29:35 -07001120 return mNotificationColor;
1121 }
1122
1123 private void updateNotificationColor() {
1124 mNotificationColor = NotificationColorUtil.resolveContrastColor(mContext,
Selim Cinekac5f0272017-05-02 16:05:41 -07001125 getStatusBarNotification().getNotification().color,
1126 getBackgroundColorWithoutTint());
Adrian Roos6f6e1592017-05-02 16:22:53 -07001127 mNotificationColorAmbient = NotificationColorUtil.resolveAmbientColor(mContext,
1128 getStatusBarNotification().getNotification().color);
Selim Cinekc897bd32016-03-18 17:32:31 -07001129 }
1130
1131 public HybridNotificationView getSingleLineView() {
1132 return mPrivateLayout.getSingleLineView();
1133 }
1134
Adrian Roos6f6e1592017-05-02 16:22:53 -07001135 public HybridNotificationView getAmbientSingleLineView() {
1136 return getShowingLayout().getAmbientSingleLineChild();
1137 }
1138
Selim Cinekf07d0622016-03-21 19:52:52 -07001139 public boolean isOnKeyguard() {
1140 return mOnKeyguard;
1141 }
1142
Selim Cinekc1e389d2016-04-07 11:02:57 -07001143 public void removeAllChildren() {
1144 List<ExpandableNotificationRow> notificationChildren
1145 = mChildrenContainer.getNotificationChildren();
1146 ArrayList<ExpandableNotificationRow> clonedList = new ArrayList<>(notificationChildren);
1147 for (int i = 0; i < clonedList.size(); i++) {
1148 ExpandableNotificationRow row = clonedList.get(i);
Selim Cinek3f19f602016-05-02 18:01:56 -07001149 if (row.keepInParent()) {
1150 continue;
1151 }
Selim Cinekc1e389d2016-04-07 11:02:57 -07001152 mChildrenContainer.removeNotification(row);
Selim Cinekc1e389d2016-04-07 11:02:57 -07001153 row.setIsChildInGroup(false, null);
1154 }
1155 onChildrenCountChanged();
1156 }
1157
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001158 public void setForceUnlocked(boolean forceUnlocked) {
1159 mForceUnlocked = forceUnlocked;
1160 if (mIsSummaryWithChildren) {
1161 List<ExpandableNotificationRow> notificationChildren = getNotificationChildren();
1162 for (ExpandableNotificationRow child : notificationChildren) {
1163 child.setForceUnlocked(forceUnlocked);
1164 }
1165 }
1166 }
1167
Selim Cinekd03518c2018-03-15 12:13:51 -07001168 public void setDismissed(boolean fromAccessibility) {
Rohan Shahc6990a02018-03-23 17:24:17 -07001169 setLongPressListener(null);
Selim Cinekd03518c2018-03-15 12:13:51 -07001170 mDismissed = true;
Selim Cineke9bad242016-06-15 11:46:37 -07001171 mGroupParentWhenDismissed = mNotificationParent;
1172 mRefocusOnDismiss = fromAccessibility;
1173 mChildAfterViewWhenDismissed = null;
Selim Cinekd03518c2018-03-15 12:13:51 -07001174 mEntry.icon.setDismissed();
Selim Cineke9bad242016-06-15 11:46:37 -07001175 if (isChildInGroup()) {
1176 List<ExpandableNotificationRow> notificationChildren =
1177 mNotificationParent.getNotificationChildren();
1178 int i = notificationChildren.indexOf(this);
1179 if (i != -1 && i < notificationChildren.size() - 1) {
1180 mChildAfterViewWhenDismissed = notificationChildren.get(i + 1);
1181 }
1182 }
Selim Cinek3f19f602016-05-02 18:01:56 -07001183 }
1184
1185 public boolean isDismissed() {
1186 return mDismissed;
1187 }
1188
1189 public boolean keepInParent() {
1190 return mKeepInParent;
1191 }
1192
1193 public void setKeepInParent(boolean keepInParent) {
1194 mKeepInParent = keepInParent;
1195 }
1196
Selim Cinekd9b7dd42017-11-10 17:53:47 -08001197 @Override
Selim Cinek3f19f602016-05-02 18:01:56 -07001198 public boolean isRemoved() {
1199 return mRemoved;
1200 }
1201
Adrian Roosd009ab12016-05-20 17:58:53 -07001202 public void setRemoved() {
1203 mRemoved = true;
Selim Cinekef8c2252017-02-10 14:52:18 -08001204 mTranslationWhenRemoved = getTranslationY();
1205 mWasChildInGroupWhenRemoved = isChildInGroup();
1206 if (isChildInGroup()) {
1207 mTranslationWhenRemoved += getNotificationParent().getTranslationY();
1208 }
Adrian Roosd009ab12016-05-20 17:58:53 -07001209 mPrivateLayout.setRemoved();
Selim Cinek3f19f602016-05-02 18:01:56 -07001210 }
1211
Selim Cinekef8c2252017-02-10 14:52:18 -08001212 public boolean wasChildInGroupWhenRemoved() {
1213 return mWasChildInGroupWhenRemoved;
1214 }
1215
1216 public float getTranslationWhenRemoved() {
1217 return mTranslationWhenRemoved;
1218 }
1219
Selim Cinekd1395642016-04-28 12:22:42 -07001220 public NotificationChildrenContainer getChildrenContainer() {
1221 return mChildrenContainer;
1222 }
1223
Selim Cinekcafa87f2016-10-26 17:00:17 -07001224 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
Selim Cinek5cf1d052017-06-01 17:36:46 -07001225 boolean wasAboveShelf = isAboveShelf();
Selim Cinek8875de12018-03-22 10:14:32 -07001226 boolean changed = headsUpAnimatingAway != mHeadsupDisappearRunning;
Selim Cinekcafa87f2016-10-26 17:00:17 -07001227 mHeadsupDisappearRunning = headsUpAnimatingAway;
1228 mPrivateLayout.setHeadsUpAnimatingAway(headsUpAnimatingAway);
Selim Cinek8875de12018-03-22 10:14:32 -07001229 if (changed && mHeadsUpAnimatingAwayListener != null) {
1230 mHeadsUpAnimatingAwayListener.accept(headsUpAnimatingAway);
1231 }
Selim Cinek5cf1d052017-06-01 17:36:46 -07001232 if (isAboveShelf() != wasAboveShelf) {
1233 mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
1234 }
Selim Cinekcafa87f2016-10-26 17:00:17 -07001235 }
1236
Selim Cinek8875de12018-03-22 10:14:32 -07001237 public void setHeadsUpAnimatingAwayListener(Consumer<Boolean> listener) {
1238 mHeadsUpAnimatingAwayListener = listener;
1239 }
1240
Selim Cinekcafa87f2016-10-26 17:00:17 -07001241 /**
1242 * @return if the view was just heads upped and is now animating away. During such a time the
1243 * layout needs to be kept consistent
1244 */
Selim Cinek29aab962018-02-27 17:05:45 -08001245 @Override
Selim Cinekcafa87f2016-10-26 17:00:17 -07001246 public boolean isHeadsUpAnimatingAway() {
1247 return mHeadsupDisappearRunning;
Selim Cinek73cf02a2016-06-17 13:08:00 -07001248 }
1249
Selim Cineke9bad242016-06-15 11:46:37 -07001250 public View getChildAfterViewWhenDismissed() {
1251 return mChildAfterViewWhenDismissed;
1252 }
1253
1254 public View getGroupParentWhenDismissed() {
1255 return mGroupParentWhenDismissed;
1256 }
1257
Rohan Shah524cf7b2018-03-15 14:40:02 -07001258 /**
1259 * Dismisses the notification with the option of showing the blocking helper in-place if we have
1260 * a negative user sentiment.
1261 *
1262 * @param fromAccessibility whether this dismiss is coming from an accessibility action
1263 * @return whether a blocking helper is shown in this row
1264 */
1265 public boolean performDismissWithBlockingHelper(boolean fromAccessibility) {
1266 NotificationBlockingHelperManager manager =
1267 Dependency.get(NotificationBlockingHelperManager.class);
1268 boolean isBlockingHelperShown = manager.perhapsShowBlockingHelper(this, mMenuRow);
1269
Rohan Shahda5dcdd2018-04-27 17:21:50 -07001270 Dependency.get(MetricsLogger.class).count(NotificationCounters.NOTIFICATION_DISMISSED, 1);
1271
Rohan Shah524cf7b2018-03-15 14:40:02 -07001272 // Continue with dismiss since we don't want the blocking helper to be directly associated
1273 // with a certain notification.
1274 performDismiss(fromAccessibility);
1275 return isBlockingHelperShown;
1276 }
1277
yoshiki iguchi85ccbbc2018-01-22 12:33:21 +09001278 public void performDismiss(boolean fromAccessibility) {
Rohan Shah524cf7b2018-03-15 14:40:02 -07001279 if (isOnlyChildInGroup()) {
yoshiki iguchi85ccbbc2018-01-22 12:33:21 +09001280 ExpandableNotificationRow groupSummary =
1281 mGroupManager.getLogicalGroupSummary(getStatusBarNotification());
1282 if (groupSummary.isClearable()) {
Rohan Shah524cf7b2018-03-15 14:40:02 -07001283 // If this is the only child in the group, dismiss the group, but don't try to show
1284 // the blocking helper affordance!
yoshiki iguchi85ccbbc2018-01-22 12:33:21 +09001285 groupSummary.performDismiss(fromAccessibility);
1286 }
1287 }
Selim Cinekd03518c2018-03-15 12:13:51 -07001288 setDismissed(fromAccessibility);
yoshiki iguchi85ccbbc2018-01-22 12:33:21 +09001289 if (isClearable()) {
1290 if (mOnDismissRunnable != null) {
1291 mOnDismissRunnable.run();
1292 }
Selim Cineke9079112016-12-14 14:41:01 -08001293 }
Selim Cinek9e624e72016-07-20 13:46:49 -07001294 }
1295
Rohan Shah524cf7b2018-03-15 14:40:02 -07001296 public void setBlockingHelperShowing(boolean isBlockingHelperShowing) {
1297 mIsBlockingHelperShowing = isBlockingHelperShowing;
1298 }
1299
1300 public boolean isBlockingHelperShowing() {
1301 return mIsBlockingHelperShowing;
1302 }
1303
Selim Cineke9079112016-12-14 14:41:01 -08001304 public void setOnDismissRunnable(Runnable onDismissRunnable) {
1305 mOnDismissRunnable = onDismissRunnable;
Selim Cinek9e624e72016-07-20 13:46:49 -07001306 }
1307
Selim Cinek281c2022016-10-13 19:14:43 -07001308 public View getNotificationIcon() {
Selim Cinek414ad332017-02-24 19:06:12 -08001309 NotificationHeaderView notificationHeader = getVisibleNotificationHeader();
Selim Cinek281c2022016-10-13 19:14:43 -07001310 if (notificationHeader != null) {
1311 return notificationHeader.getIcon();
1312 }
1313 return null;
1314 }
1315
1316 /**
1317 * @return whether the notification is currently showing a view with an icon.
1318 */
1319 public boolean isShowingIcon() {
Mady Mellor434180c2017-02-13 11:29:42 -08001320 if (areGutsExposed()) {
1321 return false;
1322 }
Selim Cinek2ffa02f2017-03-06 15:56:37 -08001323 return getVisibleNotificationHeader() != null;
Selim Cinek281c2022016-10-13 19:14:43 -07001324 }
1325
Selim Cinek0242fbb2016-10-19 13:38:32 -07001326 /**
1327 * Set how much this notification is transformed into an icon.
1328 *
Selim Cinek2b549f42016-11-22 16:38:51 -08001329 * @param contentTransformationAmount A value from 0 to 1 indicating how much we are transformed
1330 * to the content away
Selim Cinek875a3a12016-11-18 17:52:16 -08001331 * @param isLastChild is this the last child in the list. If true, then the transformation is
1332 * different since it's content fades out.
Selim Cinek0242fbb2016-10-19 13:38:32 -07001333 */
Selim Cinek2b549f42016-11-22 16:38:51 -08001334 public void setContentTransformationAmount(float contentTransformationAmount,
1335 boolean isLastChild) {
Selim Cinek875a3a12016-11-18 17:52:16 -08001336 boolean changeTransformation = isLastChild != mIsLastChild;
Selim Cinek2b549f42016-11-22 16:38:51 -08001337 changeTransformation |= mContentTransformationAmount != contentTransformationAmount;
Selim Cinek875a3a12016-11-18 17:52:16 -08001338 mIsLastChild = isLastChild;
Selim Cinek2b549f42016-11-22 16:38:51 -08001339 mContentTransformationAmount = contentTransformationAmount;
Selim Cinek875a3a12016-11-18 17:52:16 -08001340 if (changeTransformation) {
1341 updateContentTransformation();
Selim Cinek2b549f42016-11-22 16:38:51 -08001342 }
1343 }
1344
1345 /**
1346 * Set the icons to be visible of this notification.
1347 */
1348 public void setIconsVisible(boolean iconsVisible) {
1349 if (iconsVisible != mIconsVisible) {
1350 mIconsVisible = iconsVisible;
1351 updateIconVisibilities();
Selim Cinek0242fbb2016-10-19 13:38:32 -07001352 }
1353 }
1354
Selim Cinekdb167372016-11-17 15:41:17 -08001355 @Override
1356 protected void onBelowSpeedBumpChanged() {
1357 updateIconVisibilities();
1358 }
1359
Selim Cinek875a3a12016-11-18 17:52:16 -08001360 private void updateContentTransformation() {
Selim Cinek2627d722018-01-19 12:16:49 -08001361 if (mExpandAnimationRunning) {
1362 return;
1363 }
Selim Cinek875a3a12016-11-18 17:52:16 -08001364 float contentAlpha;
Selim Cinek2b549f42016-11-22 16:38:51 -08001365 float translationY = -mContentTransformationAmount * mIconTransformContentShift;
Selim Cinek875a3a12016-11-18 17:52:16 -08001366 if (mIsLastChild) {
Selim Cinek2b549f42016-11-22 16:38:51 -08001367 contentAlpha = 1.0f - mContentTransformationAmount;
Selim Cinek875a3a12016-11-18 17:52:16 -08001368 contentAlpha = Math.min(contentAlpha / 0.5f, 1.0f);
Selim Cinek0242fbb2016-10-19 13:38:32 -07001369 contentAlpha = Interpolators.ALPHA_OUT.getInterpolation(contentAlpha);
Selim Cinek875a3a12016-11-18 17:52:16 -08001370 translationY *= 0.4f;
1371 } else {
1372 contentAlpha = 1.0f;
1373 }
Adrian Rooseb434ff2017-01-11 11:18:48 -08001374 for (NotificationContentView l : mLayouts) {
1375 l.setAlpha(contentAlpha);
1376 l.setTranslationY(translationY);
1377 }
Selim Cinek875a3a12016-11-18 17:52:16 -08001378 if (mChildrenContainer != null) {
1379 mChildrenContainer.setAlpha(contentAlpha);
1380 mChildrenContainer.setTranslationY(translationY);
1381 // TODO: handle children fade out better
Selim Cinek0242fbb2016-10-19 13:38:32 -07001382 }
1383 }
1384
1385 private void updateIconVisibilities() {
Selim Cinek17e1b692016-12-02 18:19:11 -08001386 boolean visible = isChildInGroup()
1387 || (isBelowSpeedBump() && !NotificationShelf.SHOW_AMBIENT_ICONS)
1388 || mIconsVisible;
Adrian Rooseb434ff2017-01-11 11:18:48 -08001389 for (NotificationContentView l : mLayouts) {
1390 l.setIconsVisible(visible);
1391 }
Selim Cinekdb167372016-11-17 15:41:17 -08001392 if (mChildrenContainer != null) {
1393 mChildrenContainer.setIconsVisible(visible);
Selim Cinek0242fbb2016-10-19 13:38:32 -07001394 }
1395 }
1396
Selim Cinek875a3a12016-11-18 17:52:16 -08001397 /**
1398 * Get the relative top padding of a view relative to this view. This recursively walks up the
1399 * hierarchy and does the corresponding measuring.
1400 *
1401 * @param view the view to the the padding for. The requested view has to be a child of this
1402 * notification.
1403 * @return the toppadding
1404 */
1405 public int getRelativeTopPadding(View view) {
1406 int topPadding = 0;
1407 while (view.getParent() instanceof ViewGroup) {
1408 topPadding += view.getTop();
1409 view = (View) view.getParent();
1410 if (view instanceof ExpandableNotificationRow) {
1411 return topPadding;
1412 }
1413 }
1414 return topPadding;
1415 }
1416
Selim Cineka1d97902016-12-14 16:31:40 -08001417 public float getContentTranslation() {
1418 return mPrivateLayout.getTranslationY();
1419 }
1420
Selim Cinek6743c0b2017-01-18 18:24:01 -08001421 public void setIsLowPriority(boolean isLowPriority) {
1422 mIsLowPriority = isLowPriority;
1423 mPrivateLayout.setIsLowPriority(isLowPriority);
Selim Cinek1a48bab2017-02-17 19:38:40 -08001424 mNotificationInflater.setIsLowPriority(mIsLowPriority);
Selim Cinek6743c0b2017-01-18 18:24:01 -08001425 if (mChildrenContainer != null) {
1426 mChildrenContainer.setIsLowPriority(isLowPriority);
1427 }
1428 }
1429
Selim Cinek55a3e732017-05-25 18:30:10 -07001430
1431 public void setLowPriorityStateUpdated(boolean lowPriorityStateUpdated) {
1432 mLowPriorityStateUpdated = lowPriorityStateUpdated;
1433 }
1434
1435 public boolean hasLowPriorityStateUpdated() {
1436 return mLowPriorityStateUpdated;
1437 }
1438
Selim Cinek414ad332017-02-24 19:06:12 -08001439 public boolean isLowPriority() {
1440 return mIsLowPriority;
1441 }
1442
Selim Cinek7d1009b2017-01-25 15:28:28 -08001443 public void setUseIncreasedCollapsedHeight(boolean use) {
1444 mUseIncreasedCollapsedHeight = use;
Selim Cinek1a48bab2017-02-17 19:38:40 -08001445 mNotificationInflater.setUsesIncreasedHeight(use);
Selim Cinek7d1009b2017-01-25 15:28:28 -08001446 }
1447
Selim Cinek87ed69b2017-02-09 15:59:43 -08001448 public void setUseIncreasedHeadsUpHeight(boolean use) {
1449 mUseIncreasedHeadsUpHeight = use;
Selim Cinek1a48bab2017-02-17 19:38:40 -08001450 mNotificationInflater.setUsesIncreasedHeadsUpHeight(use);
1451 }
1452
1453 public void setRemoteViewClickHandler(RemoteViews.OnClickHandler remoteViewClickHandler) {
1454 mNotificationInflater.setRemoteViewClickHandler(remoteViewClickHandler);
Selim Cinek87ed69b2017-02-09 15:59:43 -08001455 }
1456
Selim Cinek5ba22542017-04-20 15:16:10 -07001457 public void setInflationCallback(InflationCallback callback) {
1458 mNotificationInflater.setInflationCallback(callback);
Selim Cinekc478f902017-02-22 20:55:44 -08001459 }
1460
Adrian Roos1a1ecfc2017-04-17 11:17:59 -07001461 public void setNeedsRedaction(boolean needsRedaction) {
1462 mNotificationInflater.setRedactAmbient(needsRedaction);
1463 }
1464
Selim Cinek5ba22542017-04-20 15:16:10 -07001465 @VisibleForTesting
1466 public NotificationInflater getNotificationInflater() {
1467 return mNotificationInflater;
1468 }
1469
Adrian Roos6f6e1592017-05-02 16:22:53 -07001470 public int getNotificationColorAmbient() {
1471 return mNotificationColorAmbient;
1472 }
1473
Chris Wren78403d72014-07-28 10:23:24 +01001474 public interface ExpansionLogger {
Anthony Chen6bf88a02017-04-10 14:41:44 -07001475 void logNotificationExpansion(String key, boolean userAction, boolean expanded);
Chris Wren78403d72014-07-28 10:23:24 +01001476 }
Selim Cinek1685e632014-04-08 02:27:49 +02001477
Chris Wren51c75102013-07-16 20:49:17 -04001478 public ExpandableNotificationRow(Context context, AttributeSet attrs) {
1479 super(context, attrs);
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -07001480 mFalsingManager = FalsingManager.getInstance(context);
Selim Cinek1a48bab2017-02-17 19:38:40 -08001481 mNotificationInflater = new NotificationInflater(this);
Mady Mellor95d743c2017-01-10 12:05:27 -08001482 mMenuRow = new NotificationMenuRow(mContext);
Selim Cinek01af3342016-02-09 19:25:31 -08001483 initDimens();
1484 }
1485
1486 private void initDimens() {
Selim Cineke62255c2017-09-28 18:23:23 -07001487 mNotificationMinHeightLegacy = NotificationUtils.getFontScaledHeight(mContext,
1488 R.dimen.notification_min_height_legacy);
Selim Cinek9b49f6d2017-11-13 18:19:35 -08001489 mNotificationMinHeightBeforeP = NotificationUtils.getFontScaledHeight(mContext,
1490 R.dimen.notification_min_height_before_p);
Selim Cineke62255c2017-09-28 18:23:23 -07001491 mNotificationMinHeight = NotificationUtils.getFontScaledHeight(mContext,
1492 R.dimen.notification_min_height);
1493 mNotificationMinHeightLarge = NotificationUtils.getFontScaledHeight(mContext,
Selim Cinek87ed69b2017-02-09 15:59:43 -08001494 R.dimen.notification_min_height_increased);
Selim Cineke62255c2017-09-28 18:23:23 -07001495 mNotificationMaxHeight = NotificationUtils.getFontScaledHeight(mContext,
1496 R.dimen.notification_max_height);
1497 mNotificationAmbientHeight = NotificationUtils.getFontScaledHeight(mContext,
1498 R.dimen.notification_ambient_height);
1499 mMaxHeadsUpHeightLegacy = NotificationUtils.getFontScaledHeight(mContext,
Selim Cinek77019c72015-12-09 10:18:02 -08001500 R.dimen.notification_max_heads_up_height_legacy);
Selim Cinek9b49f6d2017-11-13 18:19:35 -08001501 mMaxHeadsUpHeightBeforeP = NotificationUtils.getFontScaledHeight(mContext,
1502 R.dimen.notification_max_heads_up_height_before_p);
Selim Cineke62255c2017-09-28 18:23:23 -07001503 mMaxHeadsUpHeight = NotificationUtils.getFontScaledHeight(mContext,
1504 R.dimen.notification_max_heads_up_height);
1505 mMaxHeadsUpHeightIncreased = NotificationUtils.getFontScaledHeight(mContext,
Selim Cinek87ed69b2017-02-09 15:59:43 -08001506 R.dimen.notification_max_heads_up_height_increased);
Anthony Chen6bf88a02017-04-10 14:41:44 -07001507
Anthony Chen7acbb772017-04-07 16:45:25 -07001508 Resources res = getResources();
1509 mIncreasedPaddingBetweenElements = res.getDimensionPixelSize(
1510 R.dimen.notification_divider_height_increased);
1511 mIconTransformContentShiftNoIcon = res.getDimensionPixelSize(
Selim Cinek0242fbb2016-10-19 13:38:32 -07001512 R.dimen.notification_icon_transform_content_shift);
Anthony Chen6bf88a02017-04-10 14:41:44 -07001513 mEnableNonGroupedNotificationExpand =
1514 res.getBoolean(R.bool.config_enableNonGroupedNotificationExpand);
1515 mShowGroupBackgroundWhenExpanded =
1516 res.getBoolean(R.bool.config_showGroupNotificationBgWhenExpanded);
Selim Cinekf619ffc2016-02-17 14:53:05 -08001517 }
1518
1519 /**
Christoph Studera7fe6312014-06-27 19:32:44 +02001520 * Resets this view so it can be re-used for an updated notification.
1521 */
1522 public void reset() {
Jorim Jaggiae441282014-08-01 02:45:18 +02001523 mShowingPublicInitialized = false;
Selim Cinek31094df2014-08-14 19:28:15 +02001524 onHeightReset();
Selim Cinek6e28a672014-09-05 14:43:28 +02001525 requestLayout();
Christoph Studera7fe6312014-06-27 19:32:44 +02001526 }
1527
Julia Reynoldsfc640012018-02-21 12:25:27 -05001528 public void showAppOpsIcons(ArraySet<Integer> activeOps) {
1529 if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() != null) {
1530 mChildrenContainer.getHeaderView().showAppOpsIcons(activeOps);
1531 }
1532 mPrivateLayout.showAppOpsIcons(activeOps);
1533 mPublicLayout.showAppOpsIcons(activeOps);
1534 }
1535
Julia Reynoldsb5867452018-02-28 16:31:35 -05001536 public View.OnClickListener getAppOpsOnClickListener() {
1537 return mOnAppOpsClickListener;
1538 }
1539
1540 protected void setAppOpsOnClickListener(ExpandableNotificationRow.OnAppOpsClickListener l) {
1541 mOnAppOpsClickListener = v -> {
1542 createMenu();
1543 MenuItem menuItem = getProvider().getAppOpsMenuItem(mContext);
1544 if (menuItem != null) {
1545 l.onClick(this, v.getWidth() / 2, v.getHeight() / 2, menuItem);
1546 }
1547 };
1548 }
1549
Jorim Jaggi251957d2014-04-09 04:24:09 +02001550 @Override
1551 protected void onFinishInflate() {
1552 super.onFinishInflate();
Jorim Jaggibe565df2014-04-28 17:51:23 +02001553 mPublicLayout = (NotificationContentView) findViewById(R.id.expandedPublic);
1554 mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded);
Adrian Rooseb434ff2017-01-11 11:18:48 -08001555 mLayouts = new NotificationContentView[] {mPrivateLayout, mPublicLayout};
1556
1557 for (NotificationContentView l : mLayouts) {
1558 l.setExpandClickListener(mExpandClickListener);
1559 l.setContainingNotification(this);
1560 }
Selim Cinekab29aeb2015-02-20 18:18:32 +01001561 mGutsStub = (ViewStub) findViewById(R.id.notification_guts_stub);
1562 mGutsStub.setOnInflateListener(new ViewStub.OnInflateListener() {
Jorim Jaggib1cd3c12014-09-08 19:55:17 +02001563 @Override
1564 public void onInflate(ViewStub stub, View inflated) {
1565 mGuts = (NotificationGuts) inflated;
1566 mGuts.setClipTopAmount(getClipTopAmount());
1567 mGuts.setActualHeight(getActualHeight());
Selim Cinekab29aeb2015-02-20 18:18:32 +01001568 mGutsStub = null;
Jorim Jaggib1cd3c12014-09-08 19:55:17 +02001569 }
1570 });
Selim Cinekb5605e52015-02-20 18:21:41 +01001571 mChildrenContainerStub = (ViewStub) findViewById(R.id.child_container_stub);
1572 mChildrenContainerStub.setOnInflateListener(new ViewStub.OnInflateListener() {
1573
1574 @Override
1575 public void onInflate(ViewStub stub, View inflated) {
1576 mChildrenContainer = (NotificationChildrenContainer) inflated;
Selim Cinek6743c0b2017-01-18 18:24:01 -08001577 mChildrenContainer.setIsLowPriority(mIsLowPriority);
Selim Cinek414ad332017-02-24 19:06:12 -08001578 mChildrenContainer.setContainingNotification(ExpandableNotificationRow.this);
Selim Cinekc897bd32016-03-18 17:32:31 -07001579 mChildrenContainer.onNotificationUpdated();
Anthony Chen7acbb772017-04-07 16:45:25 -07001580
1581 if (mShouldTranslateContents) {
1582 mTranslateableViews.add(mChildrenContainer);
1583 }
Selim Cinekb5605e52015-02-20 18:21:41 +01001584 }
1585 });
Mady Mellor4b80b102016-01-22 08:03:58 -08001586
Anthony Chen7acbb772017-04-07 16:45:25 -07001587 if (mShouldTranslateContents) {
1588 // Add the views that we translate to reveal the menu
1589 mTranslateableViews = new ArrayList<>();
1590 for (int i = 0; i < getChildCount(); i++) {
1591 mTranslateableViews.add(getChildAt(i));
1592 }
1593 // Remove views that don't translate
1594 mTranslateableViews.remove(mChildrenContainerStub);
1595 mTranslateableViews.remove(mGutsStub);
Mady Mellor4b80b102016-01-22 08:03:58 -08001596 }
Mady Mellor4b80b102016-01-22 08:03:58 -08001597 }
1598
Geoffrey Pitsch409db272017-08-28 14:51:52 +00001599 private void doLongClickCallback() {
1600 doLongClickCallback(getWidth() / 2, getHeight() / 2);
1601 }
1602
1603 public void doLongClickCallback(int x, int y) {
1604 createMenu();
1605 MenuItem menuItem = getProvider().getLongpressMenuItem(mContext);
Mady Mellorb3a6aed2018-05-11 13:24:07 -07001606 doLongClickCallback(x, y, menuItem);
1607 }
1608
1609 private void doLongClickCallback(int x, int y, MenuItem menuItem) {
Geoffrey Pitsch409db272017-08-28 14:51:52 +00001610 if (mLongPressListener != null && menuItem != null) {
1611 mLongPressListener.onLongPress(this, x, y, menuItem);
1612 }
1613 }
1614
1615 @Override
1616 public boolean onKeyDown(int keyCode, KeyEvent event) {
1617 if (KeyEvent.isConfirmKey(keyCode)) {
1618 event.startTracking();
1619 return true;
1620 }
1621 return super.onKeyDown(keyCode, event);
1622 }
1623
1624 @Override
1625 public boolean onKeyUp(int keyCode, KeyEvent event) {
1626 if (KeyEvent.isConfirmKey(keyCode)) {
1627 if (!event.isCanceled()) {
1628 performClick();
1629 }
1630 return true;
1631 }
1632 return super.onKeyUp(keyCode, event);
1633 }
1634
1635 @Override
1636 public boolean onKeyLongPress(int keyCode, KeyEvent event) {
1637 if (KeyEvent.isConfirmKey(keyCode)) {
1638 doLongClickCallback();
1639 return true;
1640 }
1641 return false;
1642 }
1643
Mady Mellor4b80b102016-01-22 08:03:58 -08001644 public void resetTranslation() {
Mady Mellor32c638a2016-09-14 08:58:25 -07001645 if (mTranslateAnim != null) {
1646 mTranslateAnim.cancel();
1647 }
Anthony Chen7acbb772017-04-07 16:45:25 -07001648
1649 if (!mShouldTranslateContents) {
1650 setTranslationX(0);
1651 } else if (mTranslateableViews != null) {
Mady Mellor4b80b102016-01-22 08:03:58 -08001652 for (int i = 0; i < mTranslateableViews.size(); i++) {
1653 mTranslateableViews.get(i).setTranslationX(0);
1654 }
Anthony Chen7acbb772017-04-07 16:45:25 -07001655 invalidateOutline();
shawnlin4fbeac42018-04-30 20:32:40 +08001656 getEntry().expandedIcon.setScrollX(0);
Mady Mellor4b80b102016-01-22 08:03:58 -08001657 }
Anthony Chen7acbb772017-04-07 16:45:25 -07001658
Mady Mellor95d743c2017-01-10 12:05:27 -08001659 mMenuRow.resetMenu();
Mady Mellor4b80b102016-01-22 08:03:58 -08001660 }
1661
Rohan Shah56eb0912018-05-10 21:49:04 -07001662 void onGutsOpened() {
1663 resetTranslation();
1664 updateContentAccessibilityImportanceForGuts(false /* isEnabled */);
1665 }
1666
1667 void onGutsClosed() {
1668 updateContentAccessibilityImportanceForGuts(true /* isEnabled */);
1669 }
1670
1671 /**
1672 * Updates whether all the non-guts content inside this row is important for accessibility.
1673 *
1674 * @param isEnabled whether the content views should be enabled for accessibility
1675 */
1676 private void updateContentAccessibilityImportanceForGuts(boolean isEnabled) {
1677 if (mChildrenContainer != null) {
1678 updateChildAccessibilityImportance(mChildrenContainer, isEnabled);
1679 }
1680 if (mLayouts != null) {
1681 for (View view : mLayouts) {
1682 updateChildAccessibilityImportance(view, isEnabled);
1683 }
1684 }
1685
1686 if (isEnabled) {
1687 this.requestAccessibilityFocus();
1688 }
1689 }
1690
1691 /**
1692 * Updates whether the given childView is important for accessibility based on
1693 * {@code isEnabled}.
1694 */
1695 private void updateChildAccessibilityImportance(View childView, boolean isEnabled) {
1696 childView.setImportantForAccessibility(isEnabled
1697 ? View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
1698 : View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
1699 }
1700
Selim Cinekde4de0e2018-01-24 16:21:07 -08001701 public CharSequence getActiveRemoteInputText() {
1702 return mPrivateLayout.getActiveRemoteInputText();
1703 }
1704
Mady Mellor4b80b102016-01-22 08:03:58 -08001705 public void animateTranslateNotification(final float leftTarget) {
1706 if (mTranslateAnim != null) {
1707 mTranslateAnim.cancel();
1708 }
Mady Mellorb0a82462016-04-30 17:31:02 -07001709 mTranslateAnim = getTranslateViewAnimator(leftTarget, null /* updateListener */);
Mady Mellor34958fa2016-02-23 09:52:17 -08001710 if (mTranslateAnim != null) {
1711 mTranslateAnim.start();
1712 }
1713 }
1714
1715 @Override
1716 public void setTranslation(float translationX) {
1717 if (areGutsExposed()) {
1718 // Don't translate if guts are showing.
1719 return;
1720 }
Anthony Chen7acbb772017-04-07 16:45:25 -07001721 if (!mShouldTranslateContents) {
1722 setTranslationX(translationX);
1723 } else if (mTranslateableViews != null) {
1724 // Translate the group of views
1725 for (int i = 0; i < mTranslateableViews.size(); i++) {
1726 if (mTranslateableViews.get(i) != null) {
1727 mTranslateableViews.get(i).setTranslationX(translationX);
1728 }
Mady Mellor34958fa2016-02-23 09:52:17 -08001729 }
Anthony Chen7acbb772017-04-07 16:45:25 -07001730 invalidateOutline();
Selim Cinek143672c2018-03-23 20:04:32 -07001731
1732 // In order to keep the shelf in sync with this swiping, we're simply translating
1733 // it's icon by the same amount. The translation is already being used for the normal
1734 // positioning, so we can use the scrollX instead.
1735 getEntry().expandedIcon.setScrollX((int) -translationX);
Mady Mellor34958fa2016-02-23 09:52:17 -08001736 }
Mady Mellor95d743c2017-01-10 12:05:27 -08001737 if (mMenuRow.getMenuView() != null) {
1738 mMenuRow.onTranslationUpdate(translationX);
Mady Mellor34958fa2016-02-23 09:52:17 -08001739 }
1740 }
1741
1742 @Override
1743 public float getTranslation() {
Anthony Chene04afcb2017-04-21 13:51:32 -07001744 if (!mShouldTranslateContents) {
Anthony Chen7acbb772017-04-07 16:45:25 -07001745 return getTranslationX();
1746 }
1747
Mady Mellor34958fa2016-02-23 09:52:17 -08001748 if (mTranslateableViews != null && mTranslateableViews.size() > 0) {
1749 // All of the views in the list should have same translation, just use first one.
1750 return mTranslateableViews.get(0).getTranslationX();
1751 }
Anthony Chen7acbb772017-04-07 16:45:25 -07001752
Mady Mellor34958fa2016-02-23 09:52:17 -08001753 return 0;
1754 }
1755
1756 public Animator getTranslateViewAnimator(final float leftTarget,
1757 AnimatorUpdateListener listener) {
Mady Mellor723f1f92016-03-13 15:54:06 -07001758 if (mTranslateAnim != null) {
1759 mTranslateAnim.cancel();
1760 }
Mady Mellor34958fa2016-02-23 09:52:17 -08001761 if (areGutsExposed()) {
1762 // No translation if guts are exposed.
1763 return null;
1764 }
Mady Mellorb0a82462016-04-30 17:31:02 -07001765 final ObjectAnimator translateAnim = ObjectAnimator.ofFloat(this, TRANSLATE_CONTENT,
1766 leftTarget);
1767 if (listener != null) {
1768 translateAnim.addUpdateListener(listener);
Mady Mellor4b80b102016-01-22 08:03:58 -08001769 }
Mady Mellorb0a82462016-04-30 17:31:02 -07001770 translateAnim.addListener(new AnimatorListenerAdapter() {
1771 boolean cancelled = false;
1772
1773 @Override
1774 public void onAnimationCancel(Animator anim) {
1775 cancelled = true;
1776 }
1777
1778 @Override
1779 public void onAnimationEnd(Animator anim) {
Mady Mellor95d743c2017-01-10 12:05:27 -08001780 if (!cancelled && leftTarget == 0) {
1781 mMenuRow.resetMenu();
Mady Mellorb0a82462016-04-30 17:31:02 -07001782 mTranslateAnim = null;
1783 }
1784 }
1785 });
1786 mTranslateAnim = translateAnim;
1787 return translateAnim;
Mady Mellor4b80b102016-01-22 08:03:58 -08001788 }
1789
Selim Cinekab29aeb2015-02-20 18:18:32 +01001790 public void inflateGuts() {
1791 if (mGuts == null) {
1792 mGutsStub.inflate();
1793 }
1794 }
1795
Selim Cinekda42d652015-12-04 15:51:16 -08001796 private void updateChildrenVisibility() {
Selim Cinek7fa385a2018-01-24 08:35:28 -08001797 boolean hideContentWhileLaunching = mExpandAnimationRunning && mGuts != null
1798 && mGuts.isExposed();
1799 mPrivateLayout.setVisibility(!shouldShowPublic() && !mIsSummaryWithChildren
1800 && !hideContentWhileLaunching ? VISIBLE : INVISIBLE);
Selim Cinekef5127e2015-12-21 16:55:58 -08001801 if (mChildrenContainer != null) {
Selim Cinek7fa385a2018-01-24 08:35:28 -08001802 mChildrenContainer.setVisibility(!shouldShowPublic() && mIsSummaryWithChildren
1803 && !hideContentWhileLaunching ? VISIBLE
Selim Cinekef5127e2015-12-21 16:55:58 -08001804 : INVISIBLE);
Selim Cinekef5127e2015-12-21 16:55:58 -08001805 }
Selim Cinekda42d652015-12-04 15:51:16 -08001806 // The limits might have changed if the view suddenly became a group or vice versa
1807 updateLimits();
Selim Cinekb5605e52015-02-20 18:21:41 +01001808 }
1809
Jorim Jaggife40f7d2014-04-28 15:20:04 +02001810 @Override
Alan Viverettea54956a2015-01-07 16:05:02 -08001811 public boolean onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event) {
1812 if (super.onRequestSendAccessibilityEventInternal(child, event)) {
Jorim Jaggife40f7d2014-04-28 15:20:04 +02001813 // Add a record for the entire layout since its content is somehow small.
1814 // The event comes from a leaf view that is interacted with.
1815 AccessibilityEvent record = AccessibilityEvent.obtain();
1816 onInitializeAccessibilityEvent(record);
1817 dispatchPopulateAccessibilityEvent(record);
1818 event.appendRecord(record);
1819 return true;
1820 }
1821 return false;
Jorim Jaggic5dc0d02014-04-15 15:42:55 +02001822 }
Chris Wren51c75102013-07-16 20:49:17 -04001823
John Spurlocke15452b2014-08-21 09:44:39 -04001824 @Override
Jorim Jaggi4e857f42014-11-17 19:14:04 +01001825 public void setDark(boolean dark, boolean fade, long delay) {
1826 super.setDark(dark, fade, delay);
Lucas Dupincecc7c22017-09-12 16:02:45 -07001827 mDark = dark;
Adrian Roos28f90c72017-05-08 17:24:26 -07001828 if (!mIsHeadsUp) {
1829 // Only fade the showing view of the pulsing notification.
1830 fade = false;
1831 }
John Spurlocke15452b2014-08-21 09:44:39 -04001832 final NotificationContentView showing = getShowingLayout();
1833 if (showing != null) {
Jorim Jaggi4e857f42014-11-17 19:14:04 +01001834 showing.setDark(dark, fade, delay);
John Spurlocke15452b2014-08-21 09:44:39 -04001835 }
Selim Cinek9c7712d2015-12-08 19:19:48 -08001836 if (mIsSummaryWithChildren) {
Selim Cinekc897bd32016-03-18 17:32:31 -07001837 mChildrenContainer.setDark(dark, fade, delay);
Selim Cinek9c7712d2015-12-08 19:19:48 -08001838 }
Selim Cineka7c69632017-06-07 15:51:29 -07001839 updateShelfIconColor();
John Spurlocke15452b2014-08-21 09:44:39 -04001840 }
1841
Selim Cinek2627d722018-01-19 12:16:49 -08001842 public void applyExpandAnimationParams(ExpandAnimationParameters params) {
1843 if (params == null) {
1844 return;
1845 }
Selim Cinek2627d722018-01-19 12:16:49 -08001846 float zProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
1847 params.getProgress(0, 50));
1848 float translationZ = MathUtils.lerp(params.getStartTranslationZ(),
1849 mNotificationLaunchHeight,
1850 zProgress);
1851 setTranslationZ(translationZ);
Selim Cinekc25989e2018-02-16 16:42:14 -08001852 float extraWidthForClipping = params.getWidth() - getWidth()
1853 + MathUtils.lerp(0, mOutlineRadius * 2, params.getProgress());
1854 setExtraWidthForClipping(extraWidthForClipping);
1855 int top = params.getTop();
1856 float interpolation = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(params.getProgress());
1857 int startClipTopAmount = params.getStartClipTopAmount();
1858 if (mNotificationParent != null) {
Selim Cinekffd3dc62018-11-30 18:06:57 -08001859 float parentY = mNotificationParent.getTranslationY();
1860 top -= parentY;
Selim Cinekc25989e2018-02-16 16:42:14 -08001861 mNotificationParent.setTranslationZ(translationZ);
1862 int parentStartClipTopAmount = params.getParentStartClipTopAmount();
1863 if (startClipTopAmount != 0) {
1864 int clipTopAmount = (int) MathUtils.lerp(parentStartClipTopAmount,
1865 parentStartClipTopAmount - startClipTopAmount,
1866 interpolation);
1867 mNotificationParent.setClipTopAmount(clipTopAmount);
1868 }
1869 mNotificationParent.setExtraWidthForClipping(extraWidthForClipping);
Selim Cinekffd3dc62018-11-30 18:06:57 -08001870 float clipBottom = Math.max(params.getBottom(),
1871 parentY + mNotificationParent.getActualHeight()
1872 - mNotificationParent.getClipBottomAmount());
1873 float clipTop = Math.min(params.getTop(), parentY);
1874 int minimumHeightForClipping = (int) (clipBottom - clipTop);
1875 mNotificationParent.setMinimumHeightForClipping(minimumHeightForClipping);
Selim Cinekc25989e2018-02-16 16:42:14 -08001876 } else if (startClipTopAmount != 0) {
1877 int clipTopAmount = (int) MathUtils.lerp(startClipTopAmount, 0, interpolation);
1878 setClipTopAmount(clipTopAmount);
1879 }
1880 setTranslationY(top);
Selim Cinek2627d722018-01-19 12:16:49 -08001881 setActualHeight(params.getHeight());
Selim Cinekc25989e2018-02-16 16:42:14 -08001882
Selim Cinek2627d722018-01-19 12:16:49 -08001883 mBackgroundNormal.setExpandAnimationParams(params);
1884 }
1885
1886 public void setExpandAnimationRunning(boolean expandAnimationRunning) {
Selim Cinekc25989e2018-02-16 16:42:14 -08001887 View contentView;
1888 if (mIsSummaryWithChildren) {
1889 contentView = mChildrenContainer;
1890 } else {
1891 contentView = getShowingLayout();
1892 }
1893 if (mGuts != null && mGuts.isExposed()) {
1894 contentView = mGuts;
1895 }
Selim Cinek2627d722018-01-19 12:16:49 -08001896 if (expandAnimationRunning) {
Selim Cinek2627d722018-01-19 12:16:49 -08001897 contentView.animate()
1898 .alpha(0f)
1899 .setDuration(ActivityLaunchAnimator.ANIMATION_DURATION_FADE_CONTENT)
1900 .setInterpolator(Interpolators.ALPHA_OUT);
1901 setAboveShelf(true);
1902 mExpandAnimationRunning = true;
1903 mNotificationViewState.cancelAnimations(this);
1904 mNotificationLaunchHeight = AmbientState.getNotificationLaunchHeight(getContext());
1905 } else {
1906 mExpandAnimationRunning = false;
1907 setAboveShelf(isAboveShelf());
Selim Cinek7fa385a2018-01-24 08:35:28 -08001908 if (mGuts != null) {
1909 mGuts.setAlpha(1.0f);
1910 }
Selim Cinekc25989e2018-02-16 16:42:14 -08001911 if (contentView != null) {
1912 contentView.setAlpha(1.0f);
1913 }
1914 setExtraWidthForClipping(0.0f);
1915 if (mNotificationParent != null) {
1916 mNotificationParent.setExtraWidthForClipping(0.0f);
1917 mNotificationParent.setMinimumHeightForClipping(0);
1918 }
1919 }
1920 if (mNotificationParent != null) {
1921 mNotificationParent.setChildIsExpanding(mExpandAnimationRunning);
Selim Cinek2627d722018-01-19 12:16:49 -08001922 }
Selim Cinek7fa385a2018-01-24 08:35:28 -08001923 updateChildrenVisibility();
Selim Cinek2627d722018-01-19 12:16:49 -08001924 updateClipping();
1925 mBackgroundNormal.setExpandAnimationRunning(expandAnimationRunning);
1926 }
1927
Selim Cinekc25989e2018-02-16 16:42:14 -08001928 private void setChildIsExpanding(boolean isExpanding) {
1929 mChildIsExpanding = isExpanding;
Selim Cinekffd3dc62018-11-30 18:06:57 -08001930 updateClipping();
1931 invalidate();
Selim Cinekc25989e2018-02-16 16:42:14 -08001932 }
1933
1934 @Override
1935 public boolean hasExpandingChild() {
1936 return mChildIsExpanding;
1937 }
1938
Selim Cinek2627d722018-01-19 12:16:49 -08001939 @Override
1940 protected boolean shouldClipToActualHeight() {
Selim Cinekffd3dc62018-11-30 18:06:57 -08001941 return super.shouldClipToActualHeight() && !mExpandAnimationRunning;
Selim Cinek2627d722018-01-19 12:16:49 -08001942 }
1943
1944 @Override
1945 public boolean isExpandAnimationRunning() {
1946 return mExpandAnimationRunning;
1947 }
1948
Lucas Dupincecc7c22017-09-12 16:02:45 -07001949 /**
1950 * Tap sounds should not be played when we're unlocking.
1951 * Doing so would cause audio collision and the system would feel unpolished.
1952 */
1953 @Override
1954 public boolean isSoundEffectsEnabled() {
1955 final boolean mute = mDark && mSecureStateProvider != null &&
1956 !mSecureStateProvider.getAsBoolean();
1957 return !mute && super.isSoundEffectsEnabled();
1958 }
1959
Chris Wren51c75102013-07-16 20:49:17 -04001960 public boolean isExpandable() {
Selim Cinek0b9cf462017-12-07 16:31:03 -08001961 if (mIsSummaryWithChildren && !shouldShowPublic()) {
Selim Cinek388df6d2015-10-22 13:25:11 -07001962 return !mChildrenExpanded;
1963 }
Anthony Chen6bf88a02017-04-10 14:41:44 -07001964 return mEnableNonGroupedNotificationExpand && mExpandable;
Chris Wren51c75102013-07-16 20:49:17 -04001965 }
1966
1967 public void setExpandable(boolean expandable) {
1968 mExpandable = expandable;
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001969 mPrivateLayout.updateExpandButtons(isExpandable());
Chris Wren51c75102013-07-16 20:49:17 -04001970 }
1971
Selim Cinek4ffd6362015-12-29 15:12:23 +01001972 @Override
1973 public void setClipToActualHeight(boolean clipToActualHeight) {
Selim Cinek084c16b2016-01-22 17:48:22 -08001974 super.setClipToActualHeight(clipToActualHeight || isUserLocked());
1975 getShowingLayout().setClipToActualHeight(clipToActualHeight || isUserLocked());
Selim Cinek4ffd6362015-12-29 15:12:23 +01001976 }
1977
Selim Cinek1685e632014-04-08 02:27:49 +02001978 /**
1979 * @return whether the user has changed the expansion state
1980 */
1981 public boolean hasUserChangedExpansion() {
1982 return mHasUserChangedExpansion;
1983 }
1984
Chris Wren51c75102013-07-16 20:49:17 -04001985 public boolean isUserExpanded() {
1986 return mUserExpanded;
1987 }
1988
Selim Cinek1685e632014-04-08 02:27:49 +02001989 /**
1990 * Set this notification to be expanded by the user
1991 *
1992 * @param userExpanded whether the user wants this notification to be expanded
1993 */
Chris Wren51c75102013-07-16 20:49:17 -04001994 public void setUserExpanded(boolean userExpanded) {
Selim Cinek388df6d2015-10-22 13:25:11 -07001995 setUserExpanded(userExpanded, false /* allowChildExpansion */);
1996 }
1997
1998 /**
1999 * Set this notification to be expanded by the user
2000 *
2001 * @param userExpanded whether the user wants this notification to be expanded
2002 * @param allowChildExpansion whether a call to this method allows expanding children
2003 */
2004 public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -07002005 mFalsingManager.setNotificationExpanded();
Selim Cinek0b9cf462017-12-07 16:31:03 -08002006 if (mIsSummaryWithChildren && !shouldShowPublic() && allowChildExpansion
Selim Cinek414ad332017-02-24 19:06:12 -08002007 && !mChildrenContainer.showingAsLowPriority()) {
Chris Wren698b1702016-05-23 11:16:32 -04002008 final boolean wasExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
Selim Cinek388df6d2015-10-22 13:25:11 -07002009 mGroupManager.setGroupExpanded(mStatusBarNotification, userExpanded);
Selim Cinek414ad332017-02-24 19:06:12 -08002010 onExpansionChanged(true /* userAction */, wasExpanded);
Selim Cinek388df6d2015-10-22 13:25:11 -07002011 return;
2012 }
Christoph Studera7fe6312014-06-27 19:32:44 +02002013 if (userExpanded && !mExpandable) return;
Chris Wren78403d72014-07-28 10:23:24 +01002014 final boolean wasExpanded = isExpanded();
Selim Cinek1685e632014-04-08 02:27:49 +02002015 mHasUserChangedExpansion = true;
Chris Wren51c75102013-07-16 20:49:17 -04002016 mUserExpanded = userExpanded;
Selim Cinek414ad332017-02-24 19:06:12 -08002017 onExpansionChanged(true /* userAction */, wasExpanded);
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07002018 if (!wasExpanded && isExpanded()
2019 && getActualHeight() != getIntrinsicHeight()) {
2020 notifyHeightChanged(true /* needsAnimation */);
2021 }
Chris Wren51c75102013-07-16 20:49:17 -04002022 }
2023
Selim Cinekccd14fb2014-08-12 18:53:24 +02002024 public void resetUserExpansion() {
Selim Cinek414ad332017-02-24 19:06:12 -08002025 boolean changed = mUserExpanded;
Selim Cinekccd14fb2014-08-12 18:53:24 +02002026 mHasUserChangedExpansion = false;
2027 mUserExpanded = false;
Selim Cinek414ad332017-02-24 19:06:12 -08002028 if (changed && mIsSummaryWithChildren) {
2029 mChildrenContainer.onExpansionChanged();
2030 }
2031 updateShelfIconColor();
Selim Cinekccd14fb2014-08-12 18:53:24 +02002032 }
2033
Chris Wren51c75102013-07-16 20:49:17 -04002034 public boolean isUserLocked() {
Selim Cinek1b2a05e2016-04-28 14:20:39 -07002035 return mUserLocked && !mForceUnlocked;
Chris Wren51c75102013-07-16 20:49:17 -04002036 }
2037
2038 public void setUserLocked(boolean userLocked) {
2039 mUserLocked = userLocked;
Selim Cinek8f2f6a62016-02-23 19:56:31 -08002040 mPrivateLayout.setUserExpanding(userLocked);
Selim Cinek817abe72017-05-24 11:08:55 -07002041 // This is intentionally not guarded with mIsSummaryWithChildren since we might have had
2042 // children but not anymore.
2043 if (mChildrenContainer != null) {
Selim Cinek42357e02016-02-24 18:48:01 -08002044 mChildrenContainer.setUserLocked(userLocked);
Selim Cinek817abe72017-05-24 11:08:55 -07002045 if (mIsSummaryWithChildren && (userLocked || !isGroupExpanded())) {
Mady Mellorb0a82462016-04-30 17:31:02 -07002046 updateBackgroundForGroupState();
2047 }
Selim Cinek42357e02016-02-24 18:48:01 -08002048 }
Chris Wren51c75102013-07-16 20:49:17 -04002049 }
2050
Selim Cinek1685e632014-04-08 02:27:49 +02002051 /**
2052 * @return has the system set this notification to be expanded
2053 */
2054 public boolean isSystemExpanded() {
2055 return mIsSystemExpanded;
2056 }
2057
2058 /**
2059 * Set this notification to be expanded by the system.
2060 *
2061 * @param expand whether the system wants this notification to be expanded.
2062 */
2063 public void setSystemExpanded(boolean expand) {
Selim Cinek31094df2014-08-14 19:28:15 +02002064 if (expand != mIsSystemExpanded) {
2065 final boolean wasExpanded = isExpanded();
2066 mIsSystemExpanded = expand;
Selim Cinekb5605e52015-02-20 18:21:41 +01002067 notifyHeightChanged(false /* needsAnimation */);
Selim Cinek414ad332017-02-24 19:06:12 -08002068 onExpansionChanged(false /* userAction */, wasExpanded);
Selim Cineked6913b2016-06-01 12:01:17 -07002069 if (mIsSummaryWithChildren) {
Selim Cinekc897bd32016-03-18 17:32:31 -07002070 mChildrenContainer.updateGroupOverflow();
2071 }
Selim Cinek31094df2014-08-14 19:28:15 +02002072 }
Jorim Jaggidce3c4c2014-04-29 23:12:24 +02002073 }
2074
2075 /**
Selim Cinek83bc7832015-10-22 13:26:54 -07002076 * @param onKeyguard whether to prevent notification expansion
Jorim Jaggidce3c4c2014-04-29 23:12:24 +02002077 */
Selim Cinek83bc7832015-10-22 13:26:54 -07002078 public void setOnKeyguard(boolean onKeyguard) {
2079 if (onKeyguard != mOnKeyguard) {
Selim Cinek5cf1d052017-06-01 17:36:46 -07002080 boolean wasAboveShelf = isAboveShelf();
Selim Cinek31094df2014-08-14 19:28:15 +02002081 final boolean wasExpanded = isExpanded();
Selim Cinek83bc7832015-10-22 13:26:54 -07002082 mOnKeyguard = onKeyguard;
Selim Cinek414ad332017-02-24 19:06:12 -08002083 onExpansionChanged(false /* userAction */, wasExpanded);
Selim Cinek31094df2014-08-14 19:28:15 +02002084 if (wasExpanded != isExpanded()) {
Selim Cinekc897bd32016-03-18 17:32:31 -07002085 if (mIsSummaryWithChildren) {
2086 mChildrenContainer.updateGroupOverflow();
2087 }
Mady Mellor4b80b102016-01-22 08:03:58 -08002088 notifyHeightChanged(false /* needsAnimation */);
Selim Cinek31094df2014-08-14 19:28:15 +02002089 }
Selim Cinek5cf1d052017-06-01 17:36:46 -07002090 if (isAboveShelf() != wasAboveShelf) {
2091 mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
2092 }
Selim Cinek31094df2014-08-14 19:28:15 +02002093 }
Selim Cinekfe24fb72018-02-13 14:34:55 -08002094 updateRippleAllowed();
2095 }
2096
2097 private void updateRippleAllowed() {
2098 boolean allowed = isOnKeyguard()
2099 || mEntry.notification.getNotification().contentIntent == null;
2100 setRippleAllowed(allowed);
Selim Cinek1685e632014-04-08 02:27:49 +02002101 }
2102
2103 /**
Selim Cinek9e624e72016-07-20 13:46:49 -07002104 * @return Can the underlying notification be cleared? This can be different from whether the
2105 * notification can be dismissed in case notifications are sensitive on the lockscreen.
2106 * @see #canViewBeDismissed()
Dan Sandler0d3e62f2014-07-14 17:13:50 -04002107 */
2108 public boolean isClearable() {
Selim Cinek506deb62016-07-20 15:43:59 -07002109 if (mStatusBarNotification == null || !mStatusBarNotification.isClearable()) {
2110 return false;
2111 }
2112 if (mIsSummaryWithChildren) {
2113 List<ExpandableNotificationRow> notificationChildren =
2114 mChildrenContainer.getNotificationChildren();
2115 for (int i = 0; i < notificationChildren.size(); i++) {
2116 ExpandableNotificationRow child = notificationChildren.get(i);
2117 if (!child.isClearable()) {
2118 return false;
2119 }
2120 }
2121 }
2122 return true;
Dan Sandler0d3e62f2014-07-14 17:13:50 -04002123 }
2124
Jorim Jaggi9cbadd32014-05-01 20:18:31 +02002125 @Override
2126 public int getIntrinsicHeight() {
Jorim Jaggibe565df2014-04-28 17:51:23 +02002127 if (isUserLocked()) {
2128 return getActualHeight();
2129 }
Geoffrey Pitsch4dd50062016-12-06 16:41:22 -05002130 if (mGuts != null && mGuts.isExposed()) {
Mady Mellore09fb702017-03-30 13:23:29 -07002131 return mGuts.getIntrinsicHeight();
Selim Cinekd84a5932015-12-15 11:45:36 -08002132 } else if ((isChildInGroup() && !isGroupExpanded())) {
2133 return mPrivateLayout.getMinHeight();
2134 } else if (mSensitive && mHideSensitiveForIntrinsicHeight) {
2135 return getMinHeight();
Adrian Roos6f6e1592017-05-02 16:22:53 -07002136 } else if (mIsSummaryWithChildren && (!mOnKeyguard || mShowAmbient)) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08002137 return mChildrenContainer.getIntrinsicHeight();
Adrian Roos6f6e1592017-05-02 16:22:53 -07002138 } else if (isHeadsUpAllowed() && (mIsHeadsUp || mHeadsupDisappearRunning)) {
Selim Cinek73cf02a2016-06-17 13:08:00 -07002139 if (isPinned() || mHeadsupDisappearRunning) {
Selim Cinek31aada42015-12-18 17:51:15 -08002140 return getPinnedHeadsUpHeight(true /* atLeastMinHeight */);
2141 } else if (isExpanded()) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08002142 return Math.max(getMaxExpandHeight(), getHeadsUpHeight());
Selim Cinek8d490d42015-04-10 00:05:50 -07002143 } else {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08002144 return Math.max(getCollapsedHeight(), getHeadsUpHeight());
Selim Cinek8d490d42015-04-10 00:05:50 -07002145 }
Selim Cinek31aada42015-12-18 17:51:15 -08002146 } else if (isExpanded()) {
Selim Cinek83bc7832015-10-22 13:26:54 -07002147 return getMaxExpandHeight();
Selim Cinekd84a5932015-12-15 11:45:36 -08002148 } else {
Selim Cinek567e8452016-03-24 10:54:56 -07002149 return getCollapsedHeight();
Selim Cinek1685e632014-04-08 02:27:49 +02002150 }
Selim Cinekb5605e52015-02-20 18:21:41 +01002151 }
Selim Cinek1685e632014-04-08 02:27:49 +02002152
Adrian Roos6f6e1592017-05-02 16:22:53 -07002153 private boolean isHeadsUpAllowed() {
2154 return !mOnKeyguard && !mShowAmbient;
2155 }
2156
Mady Mellor43c2cd12016-12-12 21:05:13 -08002157 @Override
Mady Mellorb0a82462016-04-30 17:31:02 -07002158 public boolean isGroupExpanded() {
Selim Cinekeef84282015-10-30 16:28:00 -07002159 return mGroupManager.isGroupExpanded(mStatusBarNotification);
Selim Cinekb5605e52015-02-20 18:21:41 +01002160 }
2161
Selim Cinek263398f2015-10-21 17:40:23 -07002162 private void onChildrenCountChanged() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -05002163 mIsSummaryWithChildren = StatusBar.ENABLE_CHILD_NOTIFICATIONS
Mady Mellorb0a82462016-04-30 17:31:02 -07002164 && mChildrenContainer != null && mChildrenContainer.getNotificationChildCount() > 0;
2165 if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() == null) {
Selim Cinek414ad332017-02-24 19:06:12 -08002166 mChildrenContainer.recreateNotificationHeader(mExpandClickListener
2167 );
Selim Cinek263398f2015-10-21 17:40:23 -07002168 }
Mady Mellor6baed9e2016-05-25 16:05:09 -07002169 getShowingLayout().updateBackgroundColor(false /* animate */);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08002170 mPrivateLayout.updateExpandButtons(isExpandable());
Selim Cinekea4bef72015-12-02 15:51:10 -08002171 updateChildrenHeaderAppearance();
Selim Cinekda42d652015-12-04 15:51:16 -08002172 updateChildrenVisibility();
Selim Cinek2871bef2017-11-22 08:40:00 -08002173 applyChildrenRoundness();
Selim Cinek263398f2015-10-21 17:40:23 -07002174 }
Rohan Shah63411fc2018-03-28 19:05:52 -07002175 /**
2176 * Returns the number of channels covered by the notification row (including its children if
2177 * it's a summary notification).
2178 */
2179 public int getNumUniqueChannels() {
2180 ArraySet<NotificationChannel> channels = new ArraySet<>();
2181
2182 channels.add(mEntry.channel);
2183
2184 // If this is a summary, then add in the children notification channels for the
2185 // same user and pkg.
2186 if (mIsSummaryWithChildren) {
2187 final List<ExpandableNotificationRow> childrenRows = getNotificationChildren();
2188 final int numChildren = childrenRows.size();
2189 for (int i = 0; i < numChildren; i++) {
2190 final ExpandableNotificationRow childRow = childrenRows.get(i);
2191 final NotificationChannel childChannel = childRow.getEntry().channel;
2192 final StatusBarNotification childSbn = childRow.getStatusBarNotification();
2193 if (childSbn.getUser().equals(mStatusBarNotification.getUser()) &&
2194 childSbn.getPackageName().equals(mStatusBarNotification.getPackageName())) {
2195 channels.add(childChannel);
2196 }
2197 }
2198 }
2199 return channels.size();
2200 }
Selim Cinek263398f2015-10-21 17:40:23 -07002201
Mady Mellorb0a82462016-04-30 17:31:02 -07002202 public void updateChildrenHeaderAppearance() {
Selim Cineked6913b2016-06-01 12:01:17 -07002203 if (mIsSummaryWithChildren) {
Mady Mellorb0a82462016-04-30 17:31:02 -07002204 mChildrenContainer.updateChildrenHeaderAppearance();
2205 }
2206 }
2207
Selim Cinek1685e632014-04-08 02:27:49 +02002208 /**
2209 * Check whether the view state is currently expanded. This is given by the system in {@link
2210 * #setSystemExpanded(boolean)} and can be overridden by user expansion or
2211 * collapsing in {@link #setUserExpanded(boolean)}. Note that the visual appearance of this
2212 * view can differ from this state, if layout params are modified from outside.
2213 *
2214 * @return whether the view state is currently expanded.
2215 */
Selim Cinek83bc7832015-10-22 13:26:54 -07002216 public boolean isExpanded() {
Selim Cineke81b82b2016-03-04 11:22:28 -08002217 return isExpanded(false /* allowOnKeyguard */);
2218 }
2219
2220 public boolean isExpanded(boolean allowOnKeyguard) {
2221 return (!mOnKeyguard || allowOnKeyguard)
Selim Cinekb5605e52015-02-20 18:21:41 +01002222 && (!hasUserChangedExpansion() && (isSystemExpanded() || isSystemChildExpanded())
2223 || isUserExpanded());
2224 }
2225
2226 private boolean isSystemChildExpanded() {
2227 return mIsSystemChildExpanded;
2228 }
2229
2230 public void setSystemChildExpanded(boolean expanded) {
2231 mIsSystemChildExpanded = expanded;
Selim Cinek1685e632014-04-08 02:27:49 +02002232 }
2233
Mady Mellor035badd2017-04-04 18:45:30 -07002234 public void setLayoutListener(LayoutListener listener) {
2235 mLayoutListener = listener;
2236 }
2237
2238 public void removeListener() {
2239 mLayoutListener = null;
2240 }
2241
Selim Cinek1685e632014-04-08 02:27:49 +02002242 @Override
2243 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08002244 int intrinsicBefore = getIntrinsicHeight();
Selim Cinek1685e632014-04-08 02:27:49 +02002245 super.onLayout(changed, left, top, right, bottom);
Selim Cinekaa9db1f2018-02-27 17:35:47 -08002246 if (intrinsicBefore != getIntrinsicHeight()) {
2247 notifyHeightChanged(true /* needsAnimation */);
2248 }
Mady Mellor95d743c2017-01-10 12:05:27 -08002249 if (mMenuRow.getMenuView() != null) {
2250 mMenuRow.onHeightUpdate();
Mady Mellora6edc872016-04-26 11:01:03 -07002251 }
Selim Cinek875a3a12016-11-18 17:52:16 -08002252 updateContentShiftHeight();
Mady Mellor035badd2017-04-04 18:45:30 -07002253 if (mLayoutListener != null) {
2254 mLayoutListener.onLayout();
2255 }
Selim Cinek875a3a12016-11-18 17:52:16 -08002256 }
2257
2258 /**
2259 * Updates the content shift height such that the header is completely hidden when coming from
2260 * the top.
2261 */
2262 private void updateContentShiftHeight() {
Selim Cinek414ad332017-02-24 19:06:12 -08002263 NotificationHeaderView notificationHeader = getVisibleNotificationHeader();
Selim Cinek875a3a12016-11-18 17:52:16 -08002264 if (notificationHeader != null) {
2265 CachingIconView icon = notificationHeader.getIcon();
2266 mIconTransformContentShift = getRelativeTopPadding(icon) + icon.getHeight();
2267 } else {
2268 mIconTransformContentShift = mIconTransformContentShiftNoIcon;
2269 }
Selim Cinek1685e632014-04-08 02:27:49 +02002270 }
2271
Selim Cinekfa0a2d32016-01-14 13:02:21 -08002272 @Override
2273 public void notifyHeightChanged(boolean needsAnimation) {
2274 super.notifyHeightChanged(needsAnimation);
2275 getShowingLayout().requestSelectLayout(needsAnimation || isUserLocked());
2276 }
2277
Selim Cinek3c76d502016-02-19 15:16:33 -08002278 public void setSensitive(boolean sensitive, boolean hideSensitive) {
Jorim Jaggiae441282014-08-01 02:45:18 +02002279 mSensitive = sensitive;
Selim Cinek3c76d502016-02-19 15:16:33 -08002280 mSensitiveHiddenInGeneral = hideSensitive;
Jorim Jaggiae441282014-08-01 02:45:18 +02002281 }
2282
Mady Mellor43c2cd12016-12-12 21:05:13 -08002283 @Override
Jorim Jaggiae441282014-08-01 02:45:18 +02002284 public void setHideSensitiveForIntrinsicHeight(boolean hideSensitive) {
Selim Cinek60122be2015-04-15 18:16:50 -07002285 mHideSensitiveForIntrinsicHeight = hideSensitive;
Selim Cineka52f6a12016-02-29 15:35:58 -08002286 if (mIsSummaryWithChildren) {
2287 List<ExpandableNotificationRow> notificationChildren =
2288 mChildrenContainer.getNotificationChildren();
2289 for (int i = 0; i < notificationChildren.size(); i++) {
2290 ExpandableNotificationRow child = notificationChildren.get(i);
2291 child.setHideSensitiveForIntrinsicHeight(hideSensitive);
2292 }
2293 }
Jorim Jaggiae441282014-08-01 02:45:18 +02002294 }
2295
Mady Mellor43c2cd12016-12-12 21:05:13 -08002296 @Override
Jorim Jaggiae441282014-08-01 02:45:18 +02002297 public void setHideSensitive(boolean hideSensitive, boolean animated, long delay,
2298 long duration) {
Selim Cinek69594fb2018-05-18 12:06:10 -07002299 if (getVisibility() == GONE) {
2300 // If we are GONE, the hideSensitive parameter will not be calculated and always be
2301 // false, which is incorrect, let's wait until a real call comes in later.
2302 return;
2303 }
Jorim Jaggiae441282014-08-01 02:45:18 +02002304 boolean oldShowingPublic = mShowingPublic;
2305 mShowingPublic = mSensitive && hideSensitive;
2306 if (mShowingPublicInitialized && mShowingPublic == oldShowingPublic) {
2307 return;
2308 }
Dan Sandlera5e0f412014-01-23 15:11:54 -05002309
2310 // bail out if no public version
Selim Cinek1685e632014-04-08 02:27:49 +02002311 if (mPublicLayout.getChildCount() == 0) return;
Dan Sandlera5e0f412014-01-23 15:11:54 -05002312
Jorim Jaggiae441282014-08-01 02:45:18 +02002313 if (!animated) {
2314 mPublicLayout.animate().cancel();
2315 mPrivateLayout.animate().cancel();
Selim Cineka554c702016-06-17 18:02:12 -07002316 if (mChildrenContainer != null) {
2317 mChildrenContainer.animate().cancel();
2318 mChildrenContainer.setAlpha(1f);
2319 }
Jorim Jaggiae441282014-08-01 02:45:18 +02002320 mPublicLayout.setAlpha(1f);
2321 mPrivateLayout.setAlpha(1f);
2322 mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE);
Selim Cinekd84a5932015-12-15 11:45:36 -08002323 updateChildrenVisibility();
Jorim Jaggiae441282014-08-01 02:45:18 +02002324 } else {
Selim Cinek0b9cf462017-12-07 16:31:03 -08002325 animateShowingPublic(delay, duration, mShowingPublic);
Jorim Jaggiae441282014-08-01 02:45:18 +02002326 }
Selim Cinekc3179332016-03-04 14:44:56 -08002327 NotificationContentView showingLayout = getShowingLayout();
2328 showingLayout.updateBackgroundColor(animated);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08002329 mPrivateLayout.updateExpandButtons(isExpandable());
Lucas Dupinb6ed63b2017-05-30 16:17:42 -07002330 updateShelfIconColor();
Adrian Roose5726a22016-12-19 14:26:51 -08002331 showingLayout.setDark(isDark(), false /* animate */, 0 /* delay */);
Jorim Jaggiae441282014-08-01 02:45:18 +02002332 mShowingPublicInitialized = true;
2333 }
2334
Selim Cinek0b9cf462017-12-07 16:31:03 -08002335 private void animateShowingPublic(long delay, long duration, boolean showingPublic) {
Mady Mellorb0a82462016-04-30 17:31:02 -07002336 View[] privateViews = mIsSummaryWithChildren
2337 ? new View[] {mChildrenContainer}
Selim Cinekd84a5932015-12-15 11:45:36 -08002338 : new View[] {mPrivateLayout};
2339 View[] publicViews = new View[] {mPublicLayout};
Selim Cinek0b9cf462017-12-07 16:31:03 -08002340 View[] hiddenChildren = showingPublic ? privateViews : publicViews;
2341 View[] shownChildren = showingPublic ? publicViews : privateViews;
Selim Cinekd84a5932015-12-15 11:45:36 -08002342 for (final View hiddenView : hiddenChildren) {
2343 hiddenView.setVisibility(View.VISIBLE);
2344 hiddenView.animate().cancel();
2345 hiddenView.animate()
2346 .alpha(0f)
2347 .setStartDelay(delay)
2348 .setDuration(duration)
2349 .withEndAction(new Runnable() {
2350 @Override
2351 public void run() {
2352 hiddenView.setVisibility(View.INVISIBLE);
2353 }
2354 });
2355 }
2356 for (View showView : shownChildren) {
2357 showView.setVisibility(View.VISIBLE);
2358 showView.setAlpha(0f);
2359 showView.animate().cancel();
2360 showView.animate()
2361 .alpha(1f)
2362 .setStartDelay(delay)
2363 .setDuration(duration);
2364 }
Dan Sandler0d3e62f2014-07-14 17:13:50 -04002365 }
2366
Mady Mellor43c2cd12016-12-12 21:05:13 -08002367 @Override
Selim Cinek3776fe02016-02-04 13:32:43 -08002368 public boolean mustStayOnScreen() {
Selim Cinek9b9d6e12017-11-30 12:29:47 +01002369 return mIsHeadsUp && mMustStayOnScreen;
Selim Cinek3776fe02016-02-04 13:32:43 -08002370 }
2371
Selim Cinek9e624e72016-07-20 13:46:49 -07002372 /**
2373 * @return Whether this view is allowed to be dismissed. Only valid for visible notifications as
2374 * otherwise some state might not be updated. To request about the general clearability
2375 * see {@link #isClearable()}.
2376 */
2377 public boolean canViewBeDismissed() {
Selim Cinek0b9cf462017-12-07 16:31:03 -08002378 return isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
2379 }
2380
2381 private boolean shouldShowPublic() {
2382 return mSensitive && mHideSensitiveForIntrinsicHeight;
Dan Sandlera5e0f412014-01-23 15:11:54 -05002383 }
Jorim Jaggi251957d2014-04-09 04:24:09 +02002384
Ricky Waicd35def2016-05-03 11:07:07 +01002385 public void makeActionsVisibile() {
2386 setUserExpanded(true, true);
2387 if (isChildInGroup()) {
2388 mGroupManager.setGroupExpanded(mStatusBarNotification, true);
2389 }
Selim Cinekbb42b7d2016-08-10 13:02:38 -07002390 notifyHeightChanged(false /* needsAnimation */);
Ricky Waicd35def2016-05-03 11:07:07 +01002391 }
2392
Selim Cinekb5605e52015-02-20 18:21:41 +01002393 public void setChildrenExpanded(boolean expanded, boolean animate) {
2394 mChildrenExpanded = expanded;
Selim Cinek83bc7832015-10-22 13:26:54 -07002395 if (mChildrenContainer != null) {
2396 mChildrenContainer.setChildrenExpanded(expanded);
2397 }
Mady Mellor1a5d8ea2016-06-09 10:42:42 -07002398 updateBackgroundForGroupState();
Selim Cinekddf1b392016-05-27 16:33:10 -07002399 updateClickAndFocus();
Selim Cinekb5605e52015-02-20 18:21:41 +01002400 }
2401
Selim Cinekb5605e52015-02-20 18:21:41 +01002402 public static void applyTint(View v, int color) {
2403 int alpha;
2404 if (color != 0) {
2405 alpha = COLORED_DIVIDER_ALPHA;
2406 } else {
2407 color = 0xff000000;
2408 alpha = DEFAULT_DIVIDER_ALPHA;
2409 }
2410 if (v.getBackground() instanceof ColorDrawable) {
2411 ColorDrawable background = (ColorDrawable) v.getBackground();
2412 background.mutate();
2413 background.setColor(color);
2414 background.setAlpha(alpha);
2415 }
2416 }
2417
Selim Cinek1685e632014-04-08 02:27:49 +02002418 public int getMaxExpandHeight() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08002419 return mPrivateLayout.getExpandHeight();
2420 }
2421
2422
2423 private int getHeadsUpHeight() {
2424 return mPrivateLayout.getHeadsUpHeight();
Chris Wren51c75102013-07-16 20:49:17 -04002425 }
Jorim Jaggi584a7aa2014-04-10 23:26:13 +02002426
Mady Mellor34958fa2016-02-23 09:52:17 -08002427 public boolean areGutsExposed() {
Geoffrey Pitsch4dd50062016-12-06 16:41:22 -05002428 return (mGuts != null && mGuts.isExposed());
Mady Mellor34958fa2016-02-23 09:52:17 -08002429 }
2430
Jorim Jaggibe565df2014-04-28 17:51:23 +02002431 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02002432 public boolean isContentExpandable() {
Selim Cinek0b9cf462017-12-07 16:31:03 -08002433 if (mIsSummaryWithChildren && !shouldShowPublic()) {
Selim Cinekc0ac4af2017-03-03 15:13:48 -08002434 return true;
2435 }
Selim Cinek2f0df8a2014-06-10 17:40:42 +02002436 NotificationContentView showingLayout = getShowingLayout();
2437 return showingLayout.isContentExpandable();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02002438 }
2439
2440 @Override
Selim Cinek560e64d2015-06-09 19:58:11 -07002441 protected View getContentView() {
Selim Cinek0b9cf462017-12-07 16:31:03 -08002442 if (mIsSummaryWithChildren && !shouldShowPublic()) {
Selim Cineka5703182016-05-11 21:23:16 -04002443 return mChildrenContainer;
2444 }
Selim Cinek560e64d2015-06-09 19:58:11 -07002445 return getShowingLayout();
2446 }
2447
2448 @Override
Selim Cinekaa3901a2016-08-05 11:04:37 -07002449 protected void onAppearAnimationFinished(boolean wasAppearing) {
2450 super.onAppearAnimationFinished(wasAppearing);
2451 if (wasAppearing) {
2452 // During the animation the visible view might have changed, so let's make sure all
2453 // alphas are reset
2454 if (mChildrenContainer != null) {
2455 mChildrenContainer.setAlpha(1.0f);
2456 mChildrenContainer.setLayerType(LAYER_TYPE_NONE, null);
2457 }
Adrian Rooseb434ff2017-01-11 11:18:48 -08002458 for (NotificationContentView l : mLayouts) {
2459 l.setAlpha(1.0f);
2460 l.setLayerType(LAYER_TYPE_NONE, null);
2461 }
Selim Cinekaa3901a2016-08-05 11:04:37 -07002462 }
2463 }
2464
2465 @Override
Mady Mellorb0a82462016-04-30 17:31:02 -07002466 public int getExtraBottomPadding() {
2467 if (mIsSummaryWithChildren && isGroupExpanded()) {
2468 return mIncreasedPaddingBetweenElements;
2469 }
2470 return 0;
2471 }
2472
2473 @Override
Jorim Jaggid552d9d2014-05-07 19:41:13 +02002474 public void setActualHeight(int height, boolean notifyListeners) {
Selim Cinek4ca6c632017-02-23 18:03:37 -08002475 boolean changed = height != getActualHeight();
Selim Cinekb5605e52015-02-20 18:21:41 +01002476 super.setActualHeight(height, notifyListeners);
Selim Cinek4ca6c632017-02-23 18:03:37 -08002477 if (changed && isRemoved()) {
2478 // TODO: remove this once we found the gfx bug for this.
2479 // This is a hack since a removed view sometimes would just stay blank. it occured
2480 // when sending yourself a message and then clicking on it.
2481 ViewGroup parent = (ViewGroup) getParent();
2482 if (parent != null) {
2483 parent.invalidate();
2484 }
2485 }
Geoffrey Pitsch4dd50062016-12-06 16:41:22 -05002486 if (mGuts != null && mGuts.isExposed()) {
Mady Mellorb53bc272016-02-11 18:28:23 -08002487 mGuts.setActualHeight(height);
2488 return;
2489 }
Selim Cinekeef84282015-10-30 16:28:00 -07002490 int contentHeight = Math.max(getMinHeight(), height);
Adrian Rooseb434ff2017-01-11 11:18:48 -08002491 for (NotificationContentView l : mLayouts) {
2492 l.setContentHeight(contentHeight);
2493 }
Selim Cinek42357e02016-02-24 18:48:01 -08002494 if (mIsSummaryWithChildren) {
2495 mChildrenContainer.setActualHeight(height);
2496 }
Jorim Jaggib1cd3c12014-09-08 19:55:17 +02002497 if (mGuts != null) {
2498 mGuts.setActualHeight(height);
2499 }
Mady Mellor54540972017-06-07 11:55:36 -07002500 if (mMenuRow.getMenuView() != null) {
2501 mMenuRow.onHeightUpdate();
2502 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02002503 }
2504
2505 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01002506 public int getMaxContentHeight() {
Selim Cinek0b9cf462017-12-07 16:31:03 -08002507 if (mIsSummaryWithChildren && !shouldShowPublic()) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08002508 return mChildrenContainer.getMaxContentHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -07002509 }
Selim Cinek2f0df8a2014-06-10 17:40:42 +02002510 NotificationContentView showingLayout = getShowingLayout();
2511 return showingLayout.getMaxHeight();
Jorim Jaggibe565df2014-04-28 17:51:23 +02002512 }
2513
2514 @Override
Selim Cinekeb3fc3d2017-09-15 13:37:14 -07002515 public int getMinHeight(boolean ignoreTemporaryStates) {
2516 if (!ignoreTemporaryStates && mGuts != null && mGuts.isExposed()) {
Mady Mellore09fb702017-03-30 13:23:29 -07002517 return mGuts.getIntrinsicHeight();
Selim Cinekeb3fc3d2017-09-15 13:37:14 -07002518 } else if (!ignoreTemporaryStates && isHeadsUpAllowed() && mIsHeadsUp
2519 && mHeadsUpManager.isTrackingHeadsUp()) {
Selim Cinek31aada42015-12-18 17:51:15 -08002520 return getPinnedHeadsUpHeight(false /* atLeastMinHeight */);
Selim Cinek0b9cf462017-12-07 16:31:03 -08002521 } else if (mIsSummaryWithChildren && !isGroupExpanded() && !shouldShowPublic()) {
Selim Cinekb55386d2015-12-16 17:26:49 -08002522 return mChildrenContainer.getMinHeight();
Selim Cinekeb3fc3d2017-09-15 13:37:14 -07002523 } else if (!ignoreTemporaryStates && isHeadsUpAllowed() && mIsHeadsUp) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08002524 return getHeadsUpHeight();
Selim Cinekb55386d2015-12-16 17:26:49 -08002525 }
Selim Cinek816c8e42015-11-19 12:00:45 -08002526 NotificationContentView showingLayout = getShowingLayout();
2527 return showingLayout.getMinHeight();
2528 }
2529
2530 @Override
Selim Cinek567e8452016-03-24 10:54:56 -07002531 public int getCollapsedHeight() {
Selim Cinek0b9cf462017-12-07 16:31:03 -08002532 if (mIsSummaryWithChildren && !shouldShowPublic()) {
Selim Cinek567e8452016-03-24 10:54:56 -07002533 return mChildrenContainer.getCollapsedHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -07002534 }
Selim Cinek816c8e42015-11-19 12:00:45 -08002535 return getMinHeight();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02002536 }
2537
2538 @Override
Jorim Jaggibe565df2014-04-28 17:51:23 +02002539 public void setClipTopAmount(int clipTopAmount) {
2540 super.setClipTopAmount(clipTopAmount);
Adrian Rooseb434ff2017-01-11 11:18:48 -08002541 for (NotificationContentView l : mLayouts) {
2542 l.setClipTopAmount(clipTopAmount);
2543 }
Jorim Jaggib1cd3c12014-09-08 19:55:17 +02002544 if (mGuts != null) {
2545 mGuts.setClipTopAmount(clipTopAmount);
2546 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02002547 }
2548
Selim Cineka686b2c2016-10-26 13:58:27 -07002549 @Override
2550 public void setClipBottomAmount(int clipBottomAmount) {
Selim Cinek2627d722018-01-19 12:16:49 -08002551 if (mExpandAnimationRunning) {
2552 return;
2553 }
Selim Cinek65d418e2016-11-29 15:42:34 -08002554 if (clipBottomAmount != mClipBottomAmount) {
2555 super.setClipBottomAmount(clipBottomAmount);
Adrian Rooseb434ff2017-01-11 11:18:48 -08002556 for (NotificationContentView l : mLayouts) {
2557 l.setClipBottomAmount(clipBottomAmount);
2558 }
Selim Cinek65d418e2016-11-29 15:42:34 -08002559 if (mGuts != null) {
2560 mGuts.setClipBottomAmount(clipBottomAmount);
2561 }
Selim Cineka686b2c2016-10-26 13:58:27 -07002562 }
Selim Cinekc25989e2018-02-16 16:42:14 -08002563 if (mChildrenContainer != null && !mChildIsExpanding) {
Selim Cinek65d418e2016-11-29 15:42:34 -08002564 // We have to update this even if it hasn't changed, since the children locations can
2565 // have changed
Selim Cinekb3dadcc2016-11-21 17:21:13 -08002566 mChildrenContainer.setClipBottomAmount(clipBottomAmount);
2567 }
Selim Cineka686b2c2016-10-26 13:58:27 -07002568 }
2569
Selim Cinek42357e02016-02-24 18:48:01 -08002570 public NotificationContentView getShowingLayout() {
Selim Cinek0b9cf462017-12-07 16:31:03 -08002571 return shouldShowPublic() ? mPublicLayout : mPrivateLayout;
Selim Cinek2f0df8a2014-06-10 17:40:42 +02002572 }
Chris Wren78403d72014-07-28 10:23:24 +01002573
Selim Cinek1a48bab2017-02-17 19:38:40 -08002574 public void setLegacy(boolean legacy) {
Adrian Rooseb434ff2017-01-11 11:18:48 -08002575 for (NotificationContentView l : mLayouts) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08002576 l.setLegacy(legacy);
Adrian Rooseb434ff2017-01-11 11:18:48 -08002577 }
Jorim Jaggi59ec3042015-06-05 15:18:43 -07002578 }
2579
Selim Cineka6c6bfb2015-10-29 16:27:08 -07002580 @Override
2581 protected void updateBackgroundTint() {
2582 super.updateBackgroundTint();
Mady Mellorb0a82462016-04-30 17:31:02 -07002583 updateBackgroundForGroupState();
Selim Cineka6c6bfb2015-10-29 16:27:08 -07002584 if (mIsSummaryWithChildren) {
2585 List<ExpandableNotificationRow> notificationChildren =
2586 mChildrenContainer.getNotificationChildren();
2587 for (int i = 0; i < notificationChildren.size(); i++) {
2588 ExpandableNotificationRow child = notificationChildren.get(i);
Mady Mellorb0a82462016-04-30 17:31:02 -07002589 child.updateBackgroundForGroupState();
Selim Cineka6c6bfb2015-10-29 16:27:08 -07002590 }
2591 }
2592 }
2593
Mady Mellorb0a82462016-04-30 17:31:02 -07002594 /**
2595 * Called when a group has finished animating from collapsed or expanded state.
2596 */
2597 public void onFinishedExpansionChange() {
2598 mGroupExpansionChanging = false;
2599 updateBackgroundForGroupState();
2600 }
2601
2602 /**
2603 * Updates the parent and children backgrounds in a group based on the expansion state.
2604 */
2605 public void updateBackgroundForGroupState() {
2606 if (mIsSummaryWithChildren) {
2607 // Only when the group has finished expanding do we hide its background.
Anthony Chen6bf88a02017-04-10 14:41:44 -07002608 mShowNoBackground = !mShowGroupBackgroundWhenExpanded && isGroupExpanded()
2609 && !isGroupExpansionChanging() && !isUserLocked();
Mady Mellorb0a82462016-04-30 17:31:02 -07002610 mChildrenContainer.updateHeaderForExpansion(mShowNoBackground);
2611 List<ExpandableNotificationRow> children = mChildrenContainer.getNotificationChildren();
2612 for (int i = 0; i < children.size(); i++) {
2613 children.get(i).updateBackgroundForGroupState();
2614 }
2615 } else if (isChildInGroup()) {
2616 final int childColor = getShowingLayout().getBackgroundColorForExpansionState();
2617 // Only show a background if the group is expanded OR if it is expanding / collapsing
Anthony Chen6bf88a02017-04-10 14:41:44 -07002618 // and has a custom background color.
Mady Mellorb0a82462016-04-30 17:31:02 -07002619 final boolean showBackground = isGroupExpanded()
2620 || ((mNotificationParent.isGroupExpansionChanging()
Anthony Chen6bf88a02017-04-10 14:41:44 -07002621 || mNotificationParent.isUserLocked()) && childColor != 0);
Mady Mellorb0a82462016-04-30 17:31:02 -07002622 mShowNoBackground = !showBackground;
2623 } else {
2624 // Only children or parents ever need no background.
2625 mShowNoBackground = false;
2626 }
2627 updateOutline();
Selim Cineka6c6bfb2015-10-29 16:27:08 -07002628 updateBackground();
2629 }
2630
Adrian Roos4a579672016-05-24 16:54:37 -07002631 public int getPositionOfChild(ExpandableNotificationRow childRow) {
2632 if (mIsSummaryWithChildren) {
2633 return mChildrenContainer.getPositionInLinearLayout(childRow);
2634 }
2635 return 0;
2636 }
2637
Chris Wren78403d72014-07-28 10:23:24 +01002638 public void setExpansionLogger(ExpansionLogger logger, String key) {
2639 mLogger = logger;
2640 mLoggingKey = key;
2641 }
2642
Chris Wren6abeeb92016-05-26 14:44:38 -04002643 public void onExpandedByGesture(boolean userExpanded) {
2644 int event = MetricsEvent.ACTION_NOTIFICATION_GESTURE_EXPANDER;
2645 if (mGroupManager.isSummaryOfGroup(getStatusBarNotification())) {
2646 event = MetricsEvent.ACTION_NOTIFICATION_GROUP_GESTURE_EXPANDER;
2647 }
2648 MetricsLogger.action(mContext, event, userExpanded);
2649 }
2650
Selim Cinek6183d122016-01-14 18:48:41 -08002651 @Override
Selim Cinek42357e02016-02-24 18:48:01 -08002652 public float getIncreasedPaddingAmount() {
2653 if (mIsSummaryWithChildren) {
2654 if (isGroupExpanded()) {
2655 return 1.0f;
2656 } else if (isUserLocked()) {
Selim Cinek414ad332017-02-24 19:06:12 -08002657 return mChildrenContainer.getIncreasedPaddingAmount();
Selim Cinek42357e02016-02-24 18:48:01 -08002658 }
Selim Cinek99104832017-01-25 14:47:33 -08002659 } else if (isColorized() && (!mIsLowPriority || isExpanded())) {
Selim Cineka7ed2c12017-01-23 20:47:24 -08002660 return -1.0f;
Selim Cinek42357e02016-02-24 18:48:01 -08002661 }
2662 return 0.0f;
Selim Cinek61633a82016-01-25 15:54:10 -08002663 }
2664
Selim Cineka7ed2c12017-01-23 20:47:24 -08002665 private boolean isColorized() {
Selim Cinek99104832017-01-25 14:47:33 -08002666 return mIsColorized && mBgTint != NO_COLOR;
Selim Cineka7ed2c12017-01-23 20:47:24 -08002667 }
2668
Selim Cinek61633a82016-01-25 15:54:10 -08002669 @Override
Selim Cinek6183d122016-01-14 18:48:41 -08002670 protected boolean disallowSingleClick(MotionEvent event) {
dongwan0605.kimc4ddef42018-05-06 17:40:23 +09002671 if (areGutsExposed()) {
2672 return false;
2673 }
Selim Cinek6183d122016-01-14 18:48:41 -08002674 float x = event.getX();
2675 float y = event.getY();
Selim Cinek34eda5e2016-02-18 17:10:43 -08002676 NotificationHeaderView header = getVisibleNotificationHeader();
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07002677 if (header != null && header.isInTouchRect(x - getTranslation(), y)) {
2678 return true;
2679 }
Selim Cinek0b9cf462017-12-07 16:31:03 -08002680 if ((!mIsSummaryWithChildren || shouldShowPublic())
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07002681 && getShowingLayout().disallowSingleClick(x, y)) {
2682 return true;
Selim Cinek6183d122016-01-14 18:48:41 -08002683 }
2684 return super.disallowSingleClick(event);
2685 }
2686
Selim Cinek414ad332017-02-24 19:06:12 -08002687 private void onExpansionChanged(boolean userAction, boolean wasExpanded) {
Chris Wren698b1702016-05-23 11:16:32 -04002688 boolean nowExpanded = isExpanded();
Selim Cinek414ad332017-02-24 19:06:12 -08002689 if (mIsSummaryWithChildren && (!mIsLowPriority || wasExpanded)) {
Chris Wren698b1702016-05-23 11:16:32 -04002690 nowExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
2691 }
Selim Cinek414ad332017-02-24 19:06:12 -08002692 if (nowExpanded != wasExpanded) {
2693 updateShelfIconColor();
2694 if (mLogger != null) {
2695 mLogger.logNotificationExpansion(mLoggingKey, userAction, nowExpanded);
2696 }
2697 if (mIsSummaryWithChildren) {
2698 mChildrenContainer.onExpansionChanged();
2699 }
Chris Wren78403d72014-07-28 10:23:24 +01002700 }
2701 }
Selim Cinek570981d2015-12-01 11:37:01 -08002702
Selim Cineke9bad242016-06-15 11:46:37 -07002703 @Override
2704 public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
2705 super.onInitializeAccessibilityNodeInfoInternal(info);
Geoffrey Pitsch409db272017-08-28 14:51:52 +00002706 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
Selim Cineke9bad242016-06-15 11:46:37 -07002707 if (canViewBeDismissed()) {
2708 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS);
2709 }
Selim Cinek0b9cf462017-12-07 16:31:03 -08002710 boolean expandable = shouldShowPublic();
Selim Cinekc0ac4af2017-03-03 15:13:48 -08002711 boolean isExpanded = false;
2712 if (!expandable) {
2713 if (mIsSummaryWithChildren) {
2714 expandable = true;
2715 if (!mIsLowPriority || isExpanded()) {
2716 isExpanded = isGroupExpanded();
2717 }
2718 } else {
2719 expandable = mPrivateLayout.isContentExpandable();
2720 isExpanded = isExpanded();
2721 }
2722 }
2723 if (expandable) {
2724 if (isExpanded) {
2725 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE);
2726 } else {
2727 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND);
2728 }
2729 }
Mady Mellorb3a6aed2018-05-11 13:24:07 -07002730 NotificationMenuRowPlugin provider = getProvider();
2731 if (provider != null) {
2732 MenuItem snoozeMenu = provider.getSnoozeMenuItem(getContext());
2733 if (snoozeMenu != null) {
2734 AccessibilityAction action = new AccessibilityAction(R.id.action_snooze,
2735 getContext().getResources()
2736 .getString(R.string.notification_menu_snooze_action));
2737 info.addAction(action);
2738 }
2739 }
Selim Cineke9bad242016-06-15 11:46:37 -07002740 }
2741
2742 @Override
2743 public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
2744 if (super.performAccessibilityActionInternal(action, arguments)) {
2745 return true;
2746 }
2747 switch (action) {
2748 case AccessibilityNodeInfo.ACTION_DISMISS:
Rohan Shah524cf7b2018-03-15 14:40:02 -07002749 performDismissWithBlockingHelper(true /* fromAccessibility */);
Selim Cineke9bad242016-06-15 11:46:37 -07002750 return true;
Selim Cinekc0ac4af2017-03-03 15:13:48 -08002751 case AccessibilityNodeInfo.ACTION_COLLAPSE:
2752 case AccessibilityNodeInfo.ACTION_EXPAND:
2753 mExpandClickListener.onClick(this);
2754 return true;
Geoffrey Pitsch409db272017-08-28 14:51:52 +00002755 case AccessibilityNodeInfo.ACTION_LONG_CLICK:
2756 doLongClickCallback();
2757 return true;
Jason Monka9b2fca2018-08-09 09:38:21 -04002758 default:
2759 if (action == R.id.action_snooze) {
2760 NotificationMenuRowPlugin provider = getProvider();
2761 if (provider == null) {
2762 provider = createMenu();
2763 }
2764 MenuItem snoozeMenu = provider.getSnoozeMenuItem(getContext());
2765 if (snoozeMenu != null) {
2766 doLongClickCallback(getWidth() / 2, getHeight() / 2, snoozeMenu);
2767 }
2768 return true;
Mady Mellorb3a6aed2018-05-11 13:24:07 -07002769 }
Selim Cineke9bad242016-06-15 11:46:37 -07002770 }
2771 return false;
2772 }
2773
2774 public boolean shouldRefocusOnDismiss() {
2775 return mRefocusOnDismiss || isAccessibilityFocused();
2776 }
2777
Selim Cinek570981d2015-12-01 11:37:01 -08002778 public interface OnExpandClickListener {
Selim Cinek31aada42015-12-18 17:51:15 -08002779 void onExpandClicked(NotificationData.Entry clickedEntry, boolean nowExpanded);
Selim Cinek570981d2015-12-01 11:37:01 -08002780 }
Selim Cinekbbcebde2016-11-09 18:28:20 -08002781
2782 @Override
2783 public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
Selim Cinek2627d722018-01-19 12:16:49 -08002784 mNotificationViewState = new NotificationViewState(stackScrollState);
2785 return mNotificationViewState;
Selim Cinekbbcebde2016-11-09 18:28:20 -08002786 }
2787
Selim Cinekf93bf3e2018-05-08 14:43:21 -07002788 public NotificationViewState getViewState() {
2789 return mNotificationViewState;
2790 }
2791
Selim Cinekd127d792016-11-01 19:11:41 -07002792 @Override
2793 public boolean isAboveShelf() {
Selim Cinekc8007c52017-02-28 16:09:56 -08002794 return !isOnKeyguard()
Selim Cinek2627d722018-01-19 12:16:49 -08002795 && (mIsPinned || mHeadsupDisappearRunning || (mIsHeadsUp && mAboveShelf)
Selim Cinekc25989e2018-02-16 16:42:14 -08002796 || mExpandAnimationRunning || mChildIsExpanding);
Selim Cinekd127d792016-11-01 19:11:41 -07002797 }
2798
Adrian Roos0aac04f2016-12-08 15:59:29 -08002799 public void setShowAmbient(boolean showAmbient) {
2800 if (showAmbient != mShowAmbient) {
2801 mShowAmbient = showAmbient;
Adrian Roos6f6e1592017-05-02 16:22:53 -07002802 if (mChildrenContainer != null) {
2803 mChildrenContainer.notifyShowAmbientChanged();
2804 }
Adrian Roos0aac04f2016-12-08 15:59:29 -08002805 notifyHeightChanged(false /* needsAnimation */);
2806 }
2807 }
2808
Selim Cinek0fe07392017-11-09 13:26:34 -08002809 @Override
Tony Huangc092c432018-05-18 17:38:54 +08002810 public boolean topAmountNeedsClipping() {
2811 if (isGroupExpanded()) {
2812 return true;
2813 }
2814 if (isGroupExpansionChanging()) {
2815 return true;
2816 }
2817 if (getShowingLayout().shouldClipToRounding(true /* topRounded */,
2818 false /* bottomRounded */)) {
2819 return true;
2820 }
2821 if (mGuts != null && mGuts.getAlpha() != 0.0f) {
2822 return true;
2823 }
2824 return false;
2825 }
2826
2827 @Override
Selim Cinek515b2032017-11-15 10:20:19 -08002828 protected boolean childNeedsClipping(View child) {
2829 if (child instanceof NotificationContentView) {
2830 NotificationContentView contentView = (NotificationContentView) child;
2831 if (isClippingNeeded()) {
2832 return true;
Selim Cinek86bfcee2018-01-17 11:00:47 -08002833 } else if (!hasNoRounding()
2834 && contentView.shouldClipToRounding(getCurrentTopRoundness() != 0.0f,
2835 getCurrentBottomRoundness() != 0.0f)) {
Selim Cinek515b2032017-11-15 10:20:19 -08002836 return true;
2837 }
2838 } else if (child == mChildrenContainer) {
Selim Cinekffd3dc62018-11-30 18:06:57 -08002839 if (isClippingNeeded() || !hasNoRounding()) {
Selim Cinek515b2032017-11-15 10:20:19 -08002840 return true;
2841 }
2842 } else if (child instanceof NotificationGuts) {
Selim Cinekb95fd182017-12-21 13:03:32 -08002843 return !hasNoRounding();
Selim Cinek515b2032017-11-15 10:20:19 -08002844 }
2845 return super.childNeedsClipping(child);
2846 }
2847
2848 @Override
Selim Cinek2871bef2017-11-22 08:40:00 -08002849 protected void applyRoundness() {
2850 super.applyRoundness();
2851 applyChildrenRoundness();
2852 }
2853
2854 private void applyChildrenRoundness() {
2855 if (mIsSummaryWithChildren) {
2856 mChildrenContainer.setCurrentBottomRoundness(getCurrentBottomRoundness());
2857 }
2858 }
2859
2860 @Override
Selim Cinek515b2032017-11-15 10:20:19 -08002861 public Path getCustomClipPath(View child) {
2862 if (child instanceof NotificationGuts) {
Selim Cinek2871bef2017-11-22 08:40:00 -08002863 return getClipPath(true, /* ignoreTranslation */
2864 false /* clipRoundedToBottom */);
2865 }
2866 if (child instanceof NotificationChildrenContainer) {
2867 return getClipPath(false, /* ignoreTranslation */
2868 true /* clipRoundedToBottom */);
Selim Cinek515b2032017-11-15 10:20:19 -08002869 }
2870 return super.getCustomClipPath(child);
2871 }
2872
Selim Cinekb95fd182017-12-21 13:03:32 -08002873 private boolean hasNoRounding() {
2874 return getCurrentBottomRoundness() == 0.0f && getCurrentTopRoundness() == 0.0f;
Selim Cinek0fe07392017-11-09 13:26:34 -08002875 }
2876
Adrian Roos6f6e1592017-05-02 16:22:53 -07002877 public boolean isShowingAmbient() {
2878 return mShowAmbient;
2879 }
2880
Selim Cinekd127d792016-11-01 19:11:41 -07002881 public void setAboveShelf(boolean aboveShelf) {
Selim Cinek5cf1d052017-06-01 17:36:46 -07002882 boolean wasAboveShelf = isAboveShelf();
Selim Cinekd127d792016-11-01 19:11:41 -07002883 mAboveShelf = aboveShelf;
Selim Cinek5cf1d052017-06-01 17:36:46 -07002884 if (isAboveShelf() != wasAboveShelf) {
2885 mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
2886 }
Selim Cinekd127d792016-11-01 19:11:41 -07002887 }
2888
Selim Cinekd4776a52017-02-14 18:50:16 -08002889 public static class NotificationViewState extends ExpandableViewState {
Selim Cinekbbcebde2016-11-09 18:28:20 -08002890
2891 private final StackScrollState mOverallState;
Selim Cinek0242fbb2016-10-19 13:38:32 -07002892
Selim Cinekbbcebde2016-11-09 18:28:20 -08002893
2894 private NotificationViewState(StackScrollState stackScrollState) {
2895 mOverallState = stackScrollState;
2896 }
2897
2898 @Override
2899 public void applyToView(View view) {
Selim Cinekbbcebde2016-11-09 18:28:20 -08002900 if (view instanceof ExpandableNotificationRow) {
2901 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Selim Cinek2627d722018-01-19 12:16:49 -08002902 if (row.isExpandAnimationRunning()) {
2903 return;
2904 }
Selim Cinekc25989e2018-02-16 16:42:14 -08002905 handleFixedTranslationZ(row);
Selim Cinek2627d722018-01-19 12:16:49 -08002906 super.applyToView(view);
Selim Cinekbbcebde2016-11-09 18:28:20 -08002907 row.applyChildrenState(mOverallState);
2908 }
2909 }
Selim Cinek0cfbef42016-11-09 19:06:36 -08002910
Selim Cinekc25989e2018-02-16 16:42:14 -08002911 private void handleFixedTranslationZ(ExpandableNotificationRow row) {
2912 if (row.hasExpandingChild()) {
2913 zTranslation = row.getTranslationZ();
2914 clipTopAmount = row.getClipTopAmount();
2915 }
2916 }
2917
Selim Cinek0cfbef42016-11-09 19:06:36 -08002918 @Override
Selim Cinek2b549f42016-11-22 16:38:51 -08002919 protected void onYTranslationAnimationFinished(View view) {
2920 super.onYTranslationAnimationFinished(view);
Selim Cinekd4776a52017-02-14 18:50:16 -08002921 if (view instanceof ExpandableNotificationRow) {
2922 ExpandableNotificationRow row = (ExpandableNotificationRow) view;
2923 if (row.isHeadsUpAnimatingAway()) {
2924 row.setHeadsUpAnimatingAway(false);
2925 }
Selim Cinek0cfbef42016-11-09 19:06:36 -08002926 }
2927 }
2928
2929 @Override
2930 public void animateTo(View child, AnimationProperties properties) {
Selim Cinek0cfbef42016-11-09 19:06:36 -08002931 if (child instanceof ExpandableNotificationRow) {
2932 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
Selim Cinek2627d722018-01-19 12:16:49 -08002933 if (row.isExpandAnimationRunning()) {
2934 return;
2935 }
Selim Cinekc25989e2018-02-16 16:42:14 -08002936 handleFixedTranslationZ(row);
Selim Cinek2627d722018-01-19 12:16:49 -08002937 super.animateTo(child, properties);
Selim Cinek0cfbef42016-11-09 19:06:36 -08002938 row.startChildAnimation(mOverallState, properties);
2939 }
2940 }
Selim Cinekbbcebde2016-11-09 18:28:20 -08002941 }
Selim Cinek817abe72017-05-24 11:08:55 -07002942
2943 @VisibleForTesting
2944 protected void setChildrenContainer(NotificationChildrenContainer childrenContainer) {
2945 mChildrenContainer = childrenContainer;
2946 }
Geoffrey Pitsch409db272017-08-28 14:51:52 +00002947
Julia Reynoldsfc640012018-02-21 12:25:27 -05002948 @VisibleForTesting
2949 protected void setPrivateLayout(NotificationContentView privateLayout) {
2950 mPrivateLayout = privateLayout;
2951 }
2952
2953 @VisibleForTesting
2954 protected void setPublicLayout(NotificationContentView publicLayout) {
2955 mPublicLayout = publicLayout;
2956 }
2957
Geoffrey Pitsch409db272017-08-28 14:51:52 +00002958 /**
2959 * Equivalent to View.OnLongClickListener with coordinates
2960 */
2961 public interface LongPressListener {
2962 /**
2963 * Equivalent to {@link View.OnLongClickListener#onLongClick(View)} with coordinates
2964 * @return whether the longpress was handled
2965 */
2966 boolean onLongPress(View v, int x, int y, MenuItem item);
2967 }
Julia Reynoldsb5867452018-02-28 16:31:35 -05002968
2969 /**
2970 * Equivalent to View.OnClickListener with coordinates
2971 */
2972 public interface OnAppOpsClickListener {
2973 /**
2974 * Equivalent to {@link View.OnClickListener#onClick(View)} with coordinates
2975 * @return whether the click was handled
2976 */
2977 boolean onClick(View v, int x, int y, MenuItem item);
2978 }
Rohan Shah4ed1b2a2018-03-30 13:26:01 -07002979
2980 /**
2981 * Background task for executing IPCs to check if the notification is a system notification. The
2982 * output is used for both the blocking helper and the notification info.
2983 */
2984 private class SystemNotificationAsyncTask extends AsyncTask<Void, Void, Boolean> {
2985
2986 @Override
2987 protected Boolean doInBackground(Void... voids) {
2988 return isSystemNotification(mContext, mStatusBarNotification);
2989 }
2990
2991 @Override
2992 protected void onPostExecute(Boolean result) {
Julia Reynoldsaa96cf32018-04-17 09:09:04 -04002993 if (mEntry != null) {
2994 mEntry.mIsSystemNotification = result;
2995 }
Rohan Shah4ed1b2a2018-03-30 13:26:01 -07002996 }
2997 }
Chris Wren51c75102013-07-16 20:49:17 -04002998}