blob: 20881402952e3dba56942a9125da86b27ef25e55 [file] [log] [blame]
Chris Wren51c75102013-07-16 20:49:17 -04001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.systemui.statusbar;
18
Mady Mellor4b80b102016-01-22 08:03:58 -080019import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
Mady Mellor4b80b102016-01-22 08:03:58 -080021import android.animation.ObjectAnimator;
Mady Mellor4b80b102016-01-22 08:03:58 -080022import android.animation.ValueAnimator.AnimatorUpdateListener;
Selim Cinekddf1b392016-05-27 16:33:10 -070023import android.annotation.Nullable;
Chris Wren51c75102013-07-16 20:49:17 -040024import android.content.Context;
Selim Cinekcab4a602014-09-03 14:47:57 +020025import android.graphics.drawable.AnimatedVectorDrawable;
26import android.graphics.drawable.AnimationDrawable;
Selim Cinekb5605e52015-02-20 18:21:41 +010027import android.graphics.drawable.ColorDrawable;
Selim Cinekcab4a602014-09-03 14:47:57 +020028import android.graphics.drawable.Drawable;
Selim Cinekda42d652015-12-04 15:51:16 -080029import android.os.Build;
Selim Cineke9bad242016-06-15 11:46:37 -070030import android.os.Bundle;
Jorim Jaggib1cd3c12014-09-08 19:55:17 +020031import android.service.notification.StatusBarNotification;
Chris Wren51c75102013-07-16 20:49:17 -040032import android.util.AttributeSet;
Mady Mellorb0a82462016-04-30 17:31:02 -070033import android.util.FloatProperty;
34import android.util.Property;
Selim Cinek01af3342016-02-09 19:25:31 -080035import android.view.LayoutInflater;
Selim Cinek6183d122016-01-14 18:48:41 -080036import android.view.MotionEvent;
Selim Cinekeaa29ca2015-11-23 13:51:13 -080037import android.view.NotificationHeaderView;
Dan Sandlera5e0f412014-01-23 15:11:54 -050038import android.view.View;
Jorim Jaggib1cd3c12014-09-08 19:55:17 +020039import android.view.ViewStub;
Jorim Jaggife40f7d2014-04-28 15:20:04 +020040import android.view.accessibility.AccessibilityEvent;
Selim Cineke9bad242016-06-15 11:46:37 -070041import android.view.accessibility.AccessibilityNodeInfo;
Selim Cinek98713a42015-09-21 15:47:20 +020042import android.widget.Chronometer;
Selim Cinekcab4a602014-09-03 14:47:57 +020043import android.widget.ImageView;
Selim Cinekb5605e52015-02-20 18:21:41 +010044
Chris Wren698b1702016-05-23 11:16:32 -040045import com.android.internal.logging.MetricsLogger;
Chris Wren698b1702016-05-23 11:16:32 -040046import com.android.internal.logging.MetricsProto.MetricsEvent;
Selim Cinek4bb59342016-04-08 19:29:35 -070047import com.android.internal.util.NotificationColorUtil;
Dan Sandlera5e0f412014-01-23 15:11:54 -050048import com.android.systemui.R;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070049import com.android.systemui.classifier.FalsingManager;
Selim Cinekc897bd32016-03-18 17:32:31 -070050import com.android.systemui.statusbar.notification.HybridNotificationView;
Selim Cinekb5605e52015-02-20 18:21:41 +010051import com.android.systemui.statusbar.phone.NotificationGroupManager;
Selim Cinek31aada42015-12-18 17:51:15 -080052import com.android.systemui.statusbar.policy.HeadsUpManager;
Selim Cinekb5605e52015-02-20 18:21:41 +010053import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
Selim Cineke9bad242016-06-15 11:46:37 -070054import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
Selim Cinekb5605e52015-02-20 18:21:41 +010055import com.android.systemui.statusbar.stack.StackScrollState;
56import com.android.systemui.statusbar.stack.StackStateAnimator;
57import com.android.systemui.statusbar.stack.StackViewState;
58
Mady Mellor4b80b102016-01-22 08:03:58 -080059import java.util.ArrayList;
Selim Cinekb5605e52015-02-20 18:21:41 +010060import java.util.List;
Dan Sandlera5e0f412014-01-23 15:11:54 -050061
Jorim Jaggi4222d9a2014-04-23 16:13:15 +020062public class ExpandableNotificationRow extends ActivatableNotificationView {
Selim Cinekb5605e52015-02-20 18:21:41 +010063
64 private static final int DEFAULT_DIVIDER_ALPHA = 0x29;
65 private static final int COLORED_DIVIDER_ALPHA = 0x7B;
Selim Cinek01af3342016-02-09 19:25:31 -080066 private int mNotificationMinHeightLegacy;
67 private int mMaxHeadsUpHeightLegacy;
68 private int mMaxHeadsUpHeight;
69 private int mNotificationMinHeight;
70 private int mNotificationMaxHeight;
Mady Mellorb0a82462016-04-30 17:31:02 -070071 private int mIncreasedPaddingBetweenElements;
Chris Wren51c75102013-07-16 20:49:17 -040072
Selim Cinek1685e632014-04-08 02:27:49 +020073 /** Does this row contain layouts that can adapt to row expansion */
Chris Wren51c75102013-07-16 20:49:17 -040074 private boolean mExpandable;
Selim Cinek1685e632014-04-08 02:27:49 +020075 /** Has the user actively changed the expansion state of this row */
76 private boolean mHasUserChangedExpansion;
77 /** If {@link #mHasUserChangedExpansion}, has the user expanded this row */
Chris Wren51c75102013-07-16 20:49:17 -040078 private boolean mUserExpanded;
Selim Cinek31aada42015-12-18 17:51:15 -080079
80 /**
81 * Has this notification been expanded while it was pinned
82 */
83 private boolean mExpandedWhenPinned;
Selim Cinek1685e632014-04-08 02:27:49 +020084 /** Is the user touching this row */
Chris Wren51c75102013-07-16 20:49:17 -040085 private boolean mUserLocked;
Selim Cinek1685e632014-04-08 02:27:49 +020086 /** Are we showing the "public" version */
Dan Sandlera5e0f412014-01-23 15:11:54 -050087 private boolean mShowingPublic;
Jorim Jaggiae441282014-08-01 02:45:18 +020088 private boolean mSensitive;
Selim Cinek3c76d502016-02-19 15:16:33 -080089 private boolean mSensitiveHiddenInGeneral;
Jorim Jaggiae441282014-08-01 02:45:18 +020090 private boolean mShowingPublicInitialized;
Selim Cinek60122be2015-04-15 18:16:50 -070091 private boolean mHideSensitiveForIntrinsicHeight;
Chris Wren51c75102013-07-16 20:49:17 -040092
Selim Cinek1685e632014-04-08 02:27:49 +020093 /**
94 * Is this notification expanded by the system. The expansion state can be overridden by the
95 * user expansion.
96 */
97 private boolean mIsSystemExpanded;
Jorim Jaggidce3c4c2014-04-29 23:12:24 +020098
99 /**
Selim Cinek83bc7832015-10-22 13:26:54 -0700100 * Whether the notification is on the keyguard and the expansion is disabled.
Jorim Jaggidce3c4c2014-04-29 23:12:24 +0200101 */
Selim Cinek83bc7832015-10-22 13:26:54 -0700102 private boolean mOnKeyguard;
Jorim Jaggidce3c4c2014-04-29 23:12:24 +0200103
Mady Mellorb0a82462016-04-30 17:31:02 -0700104 private Animator mTranslateAnim;
Mady Mellor4b80b102016-01-22 08:03:58 -0800105 private ArrayList<View> mTranslateableViews;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200106 private NotificationContentView mPublicLayout;
107 private NotificationContentView mPrivateLayout;
Selim Cinek1685e632014-04-08 02:27:49 +0200108 private int mMaxExpandHeight;
Selim Cinek8d490d42015-04-10 00:05:50 -0700109 private int mHeadsUpHeight;
Selim Cinek863834b2014-05-20 04:20:25 +0200110 private View mVetoButton;
Selim Cinek4bb59342016-04-08 19:29:35 -0700111 private int mNotificationColor;
Dan Sandler0d3e62f2014-07-14 17:13:50 -0400112 private boolean mClearable;
Chris Wren78403d72014-07-28 10:23:24 +0100113 private ExpansionLogger mLogger;
114 private String mLoggingKey;
Mady Mellor4b80b102016-01-22 08:03:58 -0800115 private NotificationSettingsIconRow mSettingsIconRow;
Selim Cinek8d490d42015-04-10 00:05:50 -0700116 private NotificationGuts mGuts;
Selim Cinekda42d652015-12-04 15:51:16 -0800117 private NotificationData.Entry mEntry;
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200118 private StatusBarNotification mStatusBarNotification;
Mady Mellor3fd273e2016-03-15 21:08:14 -0700119 private String mAppName;
Selim Cinek1a521f32014-11-03 17:39:29 +0100120 private boolean mIsHeadsUp;
Selim Cinek98713a42015-09-21 15:47:20 +0200121 private boolean mLastChronometerRunning = true;
Selim Cinekb5605e52015-02-20 18:21:41 +0100122 private ViewStub mChildrenContainerStub;
123 private NotificationGroupManager mGroupManager;
Selim Cinekb5605e52015-02-20 18:21:41 +0100124 private boolean mChildrenExpanded;
Selim Cinek263398f2015-10-21 17:40:23 -0700125 private boolean mIsSummaryWithChildren;
Selim Cinekb5605e52015-02-20 18:21:41 +0100126 private NotificationChildrenContainer mChildrenContainer;
Mady Mellor4b80b102016-01-22 08:03:58 -0800127 private ViewStub mSettingsIconRowStub;
Selim Cinekab29aeb2015-02-20 18:18:32 +0100128 private ViewStub mGutsStub;
Selim Cinekb5605e52015-02-20 18:21:41 +0100129 private boolean mIsSystemChildExpanded;
Selim Cinek684a4422015-04-15 16:18:39 -0700130 private boolean mIsPinned;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700131 private FalsingManager mFalsingManager;
Selim Cinek31aada42015-12-18 17:51:15 -0800132 private HeadsUpManager mHeadsUpManager;
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200133
Jorim Jaggi5eb67c22015-08-19 19:50:49 -0700134 private boolean mJustClicked;
Selim Cinek5a175d92015-11-23 18:01:33 -0800135 private boolean mIconAnimationRunning;
Selim Cinek34d93b02015-10-22 12:30:38 -0700136 private boolean mShowNoBackground;
Selim Cinek388df6d2015-10-22 13:25:11 -0700137 private ExpandableNotificationRow mNotificationParent;
Selim Cinek570981d2015-12-01 11:37:01 -0800138 private OnExpandClickListener mOnExpandClickListener;
Mady Mellorb0a82462016-04-30 17:31:02 -0700139 private boolean mGroupExpansionChanging;
140
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800141 private OnClickListener mExpandClickListener = new OnClickListener() {
142 @Override
143 public void onClick(View v) {
Selim Cinek624c02db2015-12-14 21:00:02 -0800144 if (!mShowingPublic && mGroupManager.isSummaryOfGroup(mStatusBarNotification)) {
Chris Wren698b1702016-05-23 11:16:32 -0400145 final boolean wasExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
146 boolean nowExpanded = mGroupManager.toggleGroupExpansion(mStatusBarNotification);
147 mOnExpandClickListener.onExpandClicked(mEntry, nowExpanded);
Mady Mellorb0a82462016-04-30 17:31:02 -0700148 mGroupExpansionChanging = true;
149 updateBackgroundForGroupState();
Chris Wren698b1702016-05-23 11:16:32 -0400150 MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTIFICATION_GROUP_EXPANDER,
151 nowExpanded);
152 logExpansionEvent(true /* userAction */, wasExpanded);
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800153 } else {
Selim Cineke9bad242016-06-15 11:46:37 -0700154 if (v.isAccessibilityFocused()) {
155 mPrivateLayout.setFocusOnVisibilityChange();
156 }
Selim Cinek31aada42015-12-18 17:51:15 -0800157 boolean nowExpanded;
158 if (isPinned()) {
159 nowExpanded = !mExpandedWhenPinned;
160 mExpandedWhenPinned = nowExpanded;
161 } else {
162 nowExpanded = !isExpanded();
163 setUserExpanded(nowExpanded);
164 }
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800165 notifyHeightChanged(true);
Selim Cinek31aada42015-12-18 17:51:15 -0800166 mOnExpandClickListener.onExpandClicked(mEntry, nowExpanded);
Chris Wren698b1702016-05-23 11:16:32 -0400167 MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTIFICATION_EXPANDER,
168 nowExpanded);
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800169 }
170 }
171 };
Selim Cinek1b2a05e2016-04-28 14:20:39 -0700172 private boolean mForceUnlocked;
Selim Cinek3f19f602016-05-02 18:01:56 -0700173 private boolean mDismissed;
174 private boolean mKeepInParent;
175 private boolean mRemoved;
Mady Mellorb0a82462016-04-30 17:31:02 -0700176 private static final Property<ExpandableNotificationRow, Float> TRANSLATE_CONTENT =
177 new FloatProperty<ExpandableNotificationRow>("translate") {
178 @Override
179 public void setValue(ExpandableNotificationRow object, float value) {
180 object.setTranslation(value);
181 }
182
183 @Override
184 public Float get(ExpandableNotificationRow object) {
185 return object.getTranslation();
186 }
187 };
Selim Cinekddf1b392016-05-27 16:33:10 -0700188 private OnClickListener mOnClickListener;
Selim Cinek73cf02a2016-06-17 13:08:00 -0700189 private boolean mHeadsupDisappearRunning;
Selim Cineke9bad242016-06-15 11:46:37 -0700190 private View mChildAfterViewWhenDismissed;
191 private View mGroupParentWhenDismissed;
192 private boolean mRefocusOnDismiss;
Mady Mellorb0a82462016-04-30 17:31:02 -0700193
194 public boolean isGroupExpansionChanging() {
195 if (isChildInGroup()) {
196 return mNotificationParent.isGroupExpansionChanging();
197 }
198 return mGroupExpansionChanging;
199 }
200
201 public void setGroupExpansionChanging(boolean changing) {
202 mGroupExpansionChanging = changing;
203 }
Jorim Jaggi5eb67c22015-08-19 19:50:49 -0700204
Adrian Roos599be342016-06-13 14:54:39 -0700205 @Override
206 public void setActualHeightAnimating(boolean animating) {
207 if (mPrivateLayout != null) {
208 mPrivateLayout.setContentHeightAnimating(animating);
209 }
210 }
211
Selim Cinek8d490d42015-04-10 00:05:50 -0700212 public NotificationContentView getPrivateLayout() {
213 return mPrivateLayout;
214 }
215
216 public NotificationContentView getPublicLayout() {
217 return mPublicLayout;
218 }
219
Selim Cinekcab4a602014-09-03 14:47:57 +0200220 public void setIconAnimationRunning(boolean running) {
221 setIconAnimationRunning(running, mPublicLayout);
222 setIconAnimationRunning(running, mPrivateLayout);
Selim Cinek5a175d92015-11-23 18:01:33 -0800223 if (mIsSummaryWithChildren) {
Mady Mellorb0a82462016-04-30 17:31:02 -0700224 setIconAnimationRunningForChild(running, mChildrenContainer.getHeaderView());
Selim Cinek5a175d92015-11-23 18:01:33 -0800225 List<ExpandableNotificationRow> notificationChildren =
226 mChildrenContainer.getNotificationChildren();
227 for (int i = 0; i < notificationChildren.size(); i++) {
228 ExpandableNotificationRow child = notificationChildren.get(i);
229 child.setIconAnimationRunning(running);
230 }
231 }
232 mIconAnimationRunning = running;
Selim Cinekcab4a602014-09-03 14:47:57 +0200233 }
234
235 private void setIconAnimationRunning(boolean running, NotificationContentView layout) {
236 if (layout != null) {
237 View contractedChild = layout.getContractedChild();
238 View expandedChild = layout.getExpandedChild();
Selim Cinek8d490d42015-04-10 00:05:50 -0700239 View headsUpChild = layout.getHeadsUpChild();
Selim Cinekcab4a602014-09-03 14:47:57 +0200240 setIconAnimationRunningForChild(running, contractedChild);
241 setIconAnimationRunningForChild(running, expandedChild);
Selim Cinek8d490d42015-04-10 00:05:50 -0700242 setIconAnimationRunningForChild(running, headsUpChild);
Selim Cinekcab4a602014-09-03 14:47:57 +0200243 }
244 }
245
246 private void setIconAnimationRunningForChild(boolean running, View child) {
247 if (child != null) {
248 ImageView icon = (ImageView) child.findViewById(com.android.internal.R.id.icon);
249 setIconRunning(icon, running);
250 ImageView rightIcon = (ImageView) child.findViewById(
251 com.android.internal.R.id.right_icon);
252 setIconRunning(rightIcon, running);
253 }
254 }
255
256 private void setIconRunning(ImageView imageView, boolean running) {
257 if (imageView != null) {
258 Drawable drawable = imageView.getDrawable();
259 if (drawable instanceof AnimationDrawable) {
260 AnimationDrawable animationDrawable = (AnimationDrawable) drawable;
261 if (running) {
262 animationDrawable.start();
263 } else {
264 animationDrawable.stop();
265 }
266 } else if (drawable instanceof AnimatedVectorDrawable) {
267 AnimatedVectorDrawable animationDrawable = (AnimatedVectorDrawable) drawable;
268 if (running) {
269 animationDrawable.start();
270 } else {
271 animationDrawable.stop();
272 }
273 }
274 }
275 }
276
Selim Cinekda42d652015-12-04 15:51:16 -0800277 public void onNotificationUpdated(NotificationData.Entry entry) {
278 mEntry = entry;
279 mStatusBarNotification = entry.notification;
Adrian Roosb88b1a12015-12-09 18:51:05 -0800280 mPrivateLayout.onNotificationUpdated(entry);
281 mPublicLayout.onNotificationUpdated(entry);
Selim Cinek757d8792016-01-28 16:21:08 -0800282 mShowingPublicInitialized = false;
Selim Cinek4bb59342016-04-08 19:29:35 -0700283 updateNotificationColor();
Selim Cinek757d8792016-01-28 16:21:08 -0800284 updateClearability();
Selim Cinek8fc93c92015-11-23 17:48:07 -0800285 if (mIsSummaryWithChildren) {
Mady Mellorb0a82462016-04-30 17:31:02 -0700286 mChildrenContainer.recreateNotificationHeader(mExpandClickListener, mEntry.notification);
Selim Cinekc897bd32016-03-18 17:32:31 -0700287 mChildrenContainer.onNotificationUpdated();
Selim Cinek8fc93c92015-11-23 17:48:07 -0800288 }
Selim Cinek5a175d92015-11-23 18:01:33 -0800289 if (mIconAnimationRunning) {
290 setIconAnimationRunning(true);
291 }
Selim Cinekea4bef72015-12-02 15:51:10 -0800292 if (mNotificationParent != null) {
293 mNotificationParent.updateChildrenHeaderAppearance();
294 }
Selim Cinek263398f2015-10-21 17:40:23 -0700295 onChildrenCountChanged();
Selim Cinek624c02db2015-12-14 21:00:02 -0800296 // The public layouts expand button is always visible
297 mPublicLayout.updateExpandButtons(true);
Selim Cinekda42d652015-12-04 15:51:16 -0800298 updateLimits();
299 }
300
301 private void updateLimits() {
Selim Cineka1744872016-03-11 15:36:06 -0800302 updateLimitsForView(mPrivateLayout);
303 updateLimitsForView(mPublicLayout);
304 }
305
306 private void updateLimitsForView(NotificationContentView layout) {
307 boolean customView = layout.getContractedChild().getId()
Selim Cinekda42d652015-12-04 15:51:16 -0800308 != com.android.internal.R.id.status_bar_latest_event_content;
309 boolean beforeN = mEntry.targetSdk < Build.VERSION_CODES.N;
310 int minHeight = customView && beforeN && !mIsSummaryWithChildren ?
311 mNotificationMinHeightLegacy : mNotificationMinHeight;
Selim Cineka1744872016-03-11 15:36:06 -0800312 boolean headsUpCustom = layout.getHeadsUpChild() != null &&
313 layout.getHeadsUpChild().getId()
314 != com.android.internal.R.id.status_bar_latest_event_content;
Selim Cinek77019c72015-12-09 10:18:02 -0800315 int headsUpheight = headsUpCustom && beforeN ? mMaxHeadsUpHeightLegacy
316 : mMaxHeadsUpHeight;
Selim Cineka1744872016-03-11 15:36:06 -0800317 layout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight);
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200318 }
319
320 public StatusBarNotification getStatusBarNotification() {
321 return mStatusBarNotification;
322 }
323
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700324 public boolean isHeadsUp() {
325 return mIsHeadsUp;
326 }
327
Selim Cinek1a521f32014-11-03 17:39:29 +0100328 public void setHeadsUp(boolean isHeadsUp) {
Selim Cinekc80fdb12015-04-13 15:09:08 -0700329 int intrinsicBefore = getIntrinsicHeight();
Selim Cinek1a521f32014-11-03 17:39:29 +0100330 mIsHeadsUp = isHeadsUp;
Selim Cinek8d490d42015-04-10 00:05:50 -0700331 mPrivateLayout.setHeadsUp(isHeadsUp);
Selim Cinekb41b2f62016-04-26 14:03:29 -0700332 if (mIsSummaryWithChildren) {
333 // The overflow might change since we allow more lines as HUN.
334 mChildrenContainer.updateGroupOverflow();
335 }
Selim Cinekc80fdb12015-04-13 15:09:08 -0700336 if (intrinsicBefore != getIntrinsicHeight()) {
337 notifyHeightChanged(false /* needsAnimation */);
338 }
Selim Cinek1a521f32014-11-03 17:39:29 +0100339 }
340
Selim Cinekb5605e52015-02-20 18:21:41 +0100341 public void setGroupManager(NotificationGroupManager groupManager) {
342 mGroupManager = groupManager;
Selim Cinek83bc7832015-10-22 13:26:54 -0700343 mPrivateLayout.setGroupManager(groupManager);
Selim Cinekb5605e52015-02-20 18:21:41 +0100344 }
345
Adrian Roosb88b1a12015-12-09 18:51:05 -0800346 public void setRemoteInputController(RemoteInputController r) {
347 mPrivateLayout.setRemoteInputController(r);
348 }
349
Mady Mellor3fd273e2016-03-15 21:08:14 -0700350 public void setAppName(String appName) {
351 mAppName = appName;
352 if (mSettingsIconRow != null) {
353 mSettingsIconRow.setAppName(mAppName);
354 }
355 }
356
Selim Cinekb5605e52015-02-20 18:21:41 +0100357 public void addChildNotification(ExpandableNotificationRow row) {
358 addChildNotification(row, -1);
359 }
360
361 /**
362 * Add a child notification to this view.
363 *
364 * @param row the row to add
365 * @param childIndex the index to add it at, if -1 it will be added at the end
366 */
367 public void addChildNotification(ExpandableNotificationRow row, int childIndex) {
368 if (mChildrenContainer == null) {
369 mChildrenContainerStub.inflate();
370 }
371 mChildrenContainer.addNotification(row, childIndex);
Selim Cinek263398f2015-10-21 17:40:23 -0700372 onChildrenCountChanged();
373 row.setIsChildInGroup(true, this);
Selim Cinekb5605e52015-02-20 18:21:41 +0100374 }
375
376 public void removeChildNotification(ExpandableNotificationRow row) {
377 if (mChildrenContainer != null) {
378 mChildrenContainer.removeNotification(row);
379 }
Selim Cinek263398f2015-10-21 17:40:23 -0700380 onChildrenCountChanged();
381 row.setIsChildInGroup(false, null);
382 }
383
384 public boolean isChildInGroup() {
Selim Cineka6c6bfb2015-10-29 16:27:08 -0700385 return mNotificationParent != null;
Selim Cinek263398f2015-10-21 17:40:23 -0700386 }
387
Selim Cinek388df6d2015-10-22 13:25:11 -0700388 public ExpandableNotificationRow getNotificationParent() {
389 return mNotificationParent;
390 }
391
Selim Cinek263398f2015-10-21 17:40:23 -0700392 /**
393 * @param isChildInGroup Is this notification now in a group
394 * @param parent the new parent notification
395 */
Selim Cineka6c6bfb2015-10-29 16:27:08 -0700396 public void setIsChildInGroup(boolean isChildInGroup, ExpandableNotificationRow parent) {;
397 boolean childInGroup = BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS && isChildInGroup;
398 mNotificationParent = childInGroup ? parent : null;
399 mPrivateLayout.setIsChildInGroup(childInGroup);
Mady Mellorc7d65b42016-05-04 11:44:57 -0400400 resetBackgroundAlpha();
Mady Mellorb0a82462016-04-30 17:31:02 -0700401 updateBackgroundForGroupState();
Selim Cinekddf1b392016-05-27 16:33:10 -0700402 updateClickAndFocus();
Mady Mellorb0a82462016-04-30 17:31:02 -0700403 if (mNotificationParent != null) {
404 mNotificationParent.updateBackgroundForGroupState();
405 }
Selim Cinek34d93b02015-10-22 12:30:38 -0700406 }
407
408 @Override
Selim Cinek72109472016-01-15 16:33:22 -0800409 public boolean onTouchEvent(MotionEvent event) {
410 if (event.getActionMasked() != MotionEvent.ACTION_DOWN
411 || !isChildInGroup() || isGroupExpanded()) {
412 return super.onTouchEvent(event);
413 } else {
414 return false;
415 }
416 }
417
418 @Override
Mady Mellorf0625802016-02-11 18:03:48 -0800419 protected boolean handleSlideBack() {
420 if (mSettingsIconRow != null && mSettingsIconRow.isVisible()) {
421 animateTranslateNotification(0 /* targetLeft */);
422 return true;
423 }
424 return false;
425 }
426
427 @Override
Selim Cinek34d93b02015-10-22 12:30:38 -0700428 protected boolean shouldHideBackground() {
429 return super.shouldHideBackground() || mShowNoBackground;
Selim Cinek263398f2015-10-21 17:40:23 -0700430 }
431
432 @Override
433 public boolean isSummaryWithChildren() {
434 return mIsSummaryWithChildren;
Selim Cinekb5605e52015-02-20 18:21:41 +0100435 }
436
437 @Override
438 public boolean areChildrenExpanded() {
439 return mChildrenExpanded;
440 }
441
442 public List<ExpandableNotificationRow> getNotificationChildren() {
443 return mChildrenContainer == null ? null : mChildrenContainer.getNotificationChildren();
444 }
445
Selim Cinekeef84282015-10-30 16:28:00 -0700446 public int getNumberOfNotificationChildren() {
447 if (mChildrenContainer == null) {
448 return 0;
449 }
450 return mChildrenContainer.getNotificationChildren().size();
451 }
452
Selim Cinekb5605e52015-02-20 18:21:41 +0100453 /**
454 * Apply the order given in the list to the children.
455 *
456 * @param childOrder the new list order
457 * @return whether the list order has changed
458 */
459 public boolean applyChildOrder(List<ExpandableNotificationRow> childOrder) {
460 return mChildrenContainer != null && mChildrenContainer.applyChildOrder(childOrder);
461 }
462
463 public void getChildrenStates(StackScrollState resultState) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700464 if (mIsSummaryWithChildren) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100465 StackViewState parentState = resultState.getViewStateForView(this);
466 mChildrenContainer.getState(resultState, parentState);
467 }
468 }
469
470 public void applyChildrenState(StackScrollState state) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700471 if (mIsSummaryWithChildren) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100472 mChildrenContainer.applyState(state);
473 }
474 }
475
476 public void prepareExpansionChanged(StackScrollState state) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700477 if (mIsSummaryWithChildren) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100478 mChildrenContainer.prepareExpansionChanged(state);
479 }
480 }
481
482 public void startChildAnimation(StackScrollState finalState,
Selim Cinek99695592016-01-12 17:51:35 -0800483 StackStateAnimator stateAnimator, long delay, long duration) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700484 if (mIsSummaryWithChildren) {
Selim Cinek99695592016-01-12 17:51:35 -0800485 mChildrenContainer.startAnimationToState(finalState, stateAnimator, delay,
Selim Cinekb5605e52015-02-20 18:21:41 +0100486 duration);
487 }
488 }
489
490 public ExpandableNotificationRow getViewAtPosition(float y) {
Selim Cinek43d30f02016-03-04 10:51:32 -0800491 if (!mIsSummaryWithChildren || !mChildrenExpanded) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100492 return this;
493 } else {
494 ExpandableNotificationRow view = mChildrenContainer.getViewAtPosition(y);
495 return view == null ? this : view;
496 }
497 }
498
Selim Cinekab29aeb2015-02-20 18:18:32 +0100499 public NotificationGuts getGuts() {
500 return mGuts;
501 }
502
Selim Cinek684a4422015-04-15 16:18:39 -0700503 /**
504 * Set this notification to be pinned to the top if {@link #isHeadsUp()} is true. By doing this
505 * the notification will be rendered on top of the screen.
506 *
507 * @param pinned whether it is pinned
508 */
509 public void setPinned(boolean pinned) {
Selim Cinekdef35a82016-05-03 15:52:51 -0700510 int intrinsicHeight = getIntrinsicHeight();
Selim Cinek684a4422015-04-15 16:18:39 -0700511 mIsPinned = pinned;
Selim Cinekdef35a82016-05-03 15:52:51 -0700512 if (intrinsicHeight != getIntrinsicHeight()) {
513 notifyHeightChanged(false);
514 }
Selim Cinek31aada42015-12-18 17:51:15 -0800515 if (pinned) {
516 setIconAnimationRunning(true);
517 mExpandedWhenPinned = false;
518 } else if (mExpandedWhenPinned) {
519 setUserExpanded(true);
520 }
Selim Cinek98713a42015-09-21 15:47:20 +0200521 setChronometerRunning(mLastChronometerRunning);
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700522 }
523
Selim Cinek684a4422015-04-15 16:18:39 -0700524 public boolean isPinned() {
525 return mIsPinned;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700526 }
527
Selim Cinek31aada42015-12-18 17:51:15 -0800528 /**
529 * @param atLeastMinHeight should the value returned be at least the minimum height.
530 * Used to avoid cyclic calls
531 * @return the height of the heads up notification when pinned
532 */
533 public int getPinnedHeadsUpHeight(boolean atLeastMinHeight) {
Selim Cinek77019c72015-12-09 10:18:02 -0800534 if (mIsSummaryWithChildren) {
535 return mChildrenContainer.getIntrinsicHeight();
536 }
Selim Cinek31aada42015-12-18 17:51:15 -0800537 if(mExpandedWhenPinned) {
538 return Math.max(getMaxExpandHeight(), mHeadsUpHeight);
539 } else if (atLeastMinHeight) {
Selim Cinek567e8452016-03-24 10:54:56 -0700540 return Math.max(getCollapsedHeight(), mHeadsUpHeight);
Selim Cinek31aada42015-12-18 17:51:15 -0800541 } else {
542 return mHeadsUpHeight;
543 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700544 }
545
Jorim Jaggi5eb67c22015-08-19 19:50:49 -0700546 /**
547 * Mark whether this notification was just clicked, i.e. the user has just clicked this
548 * notification in this frame.
549 */
550 public void setJustClicked(boolean justClicked) {
551 mJustClicked = justClicked;
552 }
553
554 /**
555 * @return true if this notification has been clicked in this frame, false otherwise
556 */
557 public boolean wasJustClicked() {
558 return mJustClicked;
559 }
560
Selim Cinek98713a42015-09-21 15:47:20 +0200561 public void setChronometerRunning(boolean running) {
562 mLastChronometerRunning = running;
563 setChronometerRunning(running, mPrivateLayout);
564 setChronometerRunning(running, mPublicLayout);
565 if (mChildrenContainer != null) {
566 List<ExpandableNotificationRow> notificationChildren =
567 mChildrenContainer.getNotificationChildren();
568 for (int i = 0; i < notificationChildren.size(); i++) {
569 ExpandableNotificationRow child = notificationChildren.get(i);
570 child.setChronometerRunning(running);
571 }
572 }
573 }
574
575 private void setChronometerRunning(boolean running, NotificationContentView layout) {
576 if (layout != null) {
577 running = running || isPinned();
578 View contractedChild = layout.getContractedChild();
579 View expandedChild = layout.getExpandedChild();
580 View headsUpChild = layout.getHeadsUpChild();
581 setChronometerRunningForChild(running, contractedChild);
582 setChronometerRunningForChild(running, expandedChild);
583 setChronometerRunningForChild(running, headsUpChild);
584 }
585 }
586
587 private void setChronometerRunningForChild(boolean running, View child) {
588 if (child != null) {
589 View chronometer = child.findViewById(com.android.internal.R.id.chronometer);
590 if (chronometer instanceof Chronometer) {
591 ((Chronometer) chronometer).setStarted(running);
592 }
593 }
594 }
595
Selim Cinekea4bef72015-12-02 15:51:10 -0800596 public NotificationHeaderView getNotificationHeader() {
Mady Mellorb0a82462016-04-30 17:31:02 -0700597 if (mIsSummaryWithChildren) {
598 return mChildrenContainer.getHeaderView();
Selim Cinek8d6440d2015-10-22 13:00:05 -0700599 }
Selim Cinekea4bef72015-12-02 15:51:10 -0800600 return mPrivateLayout.getNotificationHeader();
Selim Cinek8d6440d2015-10-22 13:00:05 -0700601 }
602
Selim Cinek34eda5e2016-02-18 17:10:43 -0800603 private NotificationHeaderView getVisibleNotificationHeader() {
Mady Mellorb0a82462016-04-30 17:31:02 -0700604 if (mIsSummaryWithChildren) {
605 return mChildrenContainer.getHeaderView();
Selim Cinek34eda5e2016-02-18 17:10:43 -0800606 }
607 return getShowingLayout().getVisibleNotificationHeader();
608 }
609
Selim Cinek570981d2015-12-01 11:37:01 -0800610 public void setOnExpandClickListener(OnExpandClickListener onExpandClickListener) {
611 mOnExpandClickListener = onExpandClickListener;
612 }
613
Selim Cinekddf1b392016-05-27 16:33:10 -0700614 @Override
615 public void setOnClickListener(@Nullable OnClickListener l) {
616 super.setOnClickListener(l);
617 mOnClickListener = l;
618 updateClickAndFocus();
619 }
620
621 private void updateClickAndFocus() {
622 boolean normalChild = !isChildInGroup() || isGroupExpanded();
623 boolean clickable = mOnClickListener != null && normalChild;
624 if (isFocusable() != normalChild) {
625 setFocusable(normalChild);
626 }
627 if (isClickable() != clickable) {
628 setClickable(clickable);
629 }
630 }
631
Selim Cinek31aada42015-12-18 17:51:15 -0800632 public void setHeadsUpManager(HeadsUpManager headsUpManager) {
633 mHeadsUpManager = headsUpManager;
634 }
635
Selim Cinek01af3342016-02-09 19:25:31 -0800636 public void reInflateViews() {
637 initDimens();
638 if (mIsSummaryWithChildren) {
Selim Cinek01af3342016-02-09 19:25:31 -0800639 if (mChildrenContainer != null) {
Mady Mellorb0a82462016-04-30 17:31:02 -0700640 mChildrenContainer.reInflateViews(mExpandClickListener, mEntry.notification);
Selim Cinek01af3342016-02-09 19:25:31 -0800641 }
642 }
643 if (mGuts != null) {
644 View oldGuts = mGuts;
645 int index = indexOfChild(oldGuts);
646 removeView(oldGuts);
647 mGuts = (NotificationGuts) LayoutInflater.from(mContext).inflate(
648 R.layout.notification_guts, this, false);
649 mGuts.setVisibility(oldGuts.getVisibility());
650 addView(mGuts, index);
651 }
Mady Mellor4b80b102016-01-22 08:03:58 -0800652 if (mSettingsIconRow != null) {
653 View oldSettings = mSettingsIconRow;
654 int settingsIndex = indexOfChild(oldSettings);
655 removeView(oldSettings);
656 mSettingsIconRow = (NotificationSettingsIconRow) LayoutInflater.from(mContext).inflate(
657 R.layout.notification_settings_icon_row, this, false);
658 mSettingsIconRow.setNotificationRowParent(ExpandableNotificationRow.this);
Mady Mellor3fd273e2016-03-15 21:08:14 -0700659 mSettingsIconRow.setAppName(mAppName);
Mady Mellor4b80b102016-01-22 08:03:58 -0800660 mSettingsIconRow.setVisibility(oldSettings.getVisibility());
661 addView(mSettingsIconRow, settingsIndex);
662
663 }
Selim Cinekde33a4a2016-02-11 16:43:41 -0800664 mPrivateLayout.reInflateViews();
665 mPublicLayout.reInflateViews();
Selim Cinek01af3342016-02-09 19:25:31 -0800666 }
667
Selim Cinekc3179332016-03-04 14:44:56 -0800668 public void setContentBackground(int customBackgroundColor, boolean animate,
669 NotificationContentView notificationContentView) {
670 if (getShowingLayout() == notificationContentView) {
671 setTintColor(customBackgroundColor, animate);
672 }
673 }
674
Adrian Roos0bd8a4b2016-03-14 16:21:44 -0700675 public void closeRemoteInput() {
676 mPrivateLayout.closeRemoteInput();
677 mPublicLayout.closeRemoteInput();
678 }
679
Selim Cinekc897bd32016-03-18 17:32:31 -0700680 /**
681 * Set by how much the single line view should be indented.
682 */
683 public void setSingleLineWidthIndention(int indention) {
684 mPrivateLayout.setSingleLineWidthIndention(indention);
685 }
686
687 public int getNotificationColor() {
Selim Cinek4bb59342016-04-08 19:29:35 -0700688 return mNotificationColor;
689 }
690
691 private void updateNotificationColor() {
692 mNotificationColor = NotificationColorUtil.resolveContrastColor(mContext,
693 getStatusBarNotification().getNotification().color);
Selim Cinekc897bd32016-03-18 17:32:31 -0700694 }
695
696 public HybridNotificationView getSingleLineView() {
697 return mPrivateLayout.getSingleLineView();
698 }
699
Selim Cinekf07d0622016-03-21 19:52:52 -0700700 public boolean isOnKeyguard() {
701 return mOnKeyguard;
702 }
703
Selim Cinekc1e389d2016-04-07 11:02:57 -0700704 public void removeAllChildren() {
705 List<ExpandableNotificationRow> notificationChildren
706 = mChildrenContainer.getNotificationChildren();
707 ArrayList<ExpandableNotificationRow> clonedList = new ArrayList<>(notificationChildren);
708 for (int i = 0; i < clonedList.size(); i++) {
709 ExpandableNotificationRow row = clonedList.get(i);
Selim Cinek3f19f602016-05-02 18:01:56 -0700710 if (row.keepInParent()) {
711 continue;
712 }
Selim Cinekc1e389d2016-04-07 11:02:57 -0700713 mChildrenContainer.removeNotification(row);
Selim Cinekc1e389d2016-04-07 11:02:57 -0700714 row.setIsChildInGroup(false, null);
715 }
716 onChildrenCountChanged();
717 }
718
Selim Cinek1b2a05e2016-04-28 14:20:39 -0700719 public void setForceUnlocked(boolean forceUnlocked) {
720 mForceUnlocked = forceUnlocked;
721 if (mIsSummaryWithChildren) {
722 List<ExpandableNotificationRow> notificationChildren = getNotificationChildren();
723 for (ExpandableNotificationRow child : notificationChildren) {
724 child.setForceUnlocked(forceUnlocked);
725 }
726 }
727 }
728
Selim Cineke9bad242016-06-15 11:46:37 -0700729 public void setDismissed(boolean dismissed, boolean fromAccessibility) {
Selim Cinek3f19f602016-05-02 18:01:56 -0700730 mDismissed = dismissed;
Selim Cineke9bad242016-06-15 11:46:37 -0700731 mGroupParentWhenDismissed = mNotificationParent;
732 mRefocusOnDismiss = fromAccessibility;
733 mChildAfterViewWhenDismissed = null;
734 if (isChildInGroup()) {
735 List<ExpandableNotificationRow> notificationChildren =
736 mNotificationParent.getNotificationChildren();
737 int i = notificationChildren.indexOf(this);
738 if (i != -1 && i < notificationChildren.size() - 1) {
739 mChildAfterViewWhenDismissed = notificationChildren.get(i + 1);
740 }
741 }
Selim Cinek3f19f602016-05-02 18:01:56 -0700742 }
743
744 public boolean isDismissed() {
745 return mDismissed;
746 }
747
748 public boolean keepInParent() {
749 return mKeepInParent;
750 }
751
752 public void setKeepInParent(boolean keepInParent) {
753 mKeepInParent = keepInParent;
754 }
755
756 public boolean isRemoved() {
757 return mRemoved;
758 }
759
Adrian Roosd009ab12016-05-20 17:58:53 -0700760 public void setRemoved() {
761 mRemoved = true;
762
763 mPrivateLayout.setRemoved();
764 if (mChildrenContainer != null) {
765 mChildrenContainer.setRemoved();
766 }
Selim Cinek3f19f602016-05-02 18:01:56 -0700767 }
768
Selim Cinekd1395642016-04-28 12:22:42 -0700769 public NotificationChildrenContainer getChildrenContainer() {
770 return mChildrenContainer;
771 }
772
Selim Cinek73cf02a2016-06-17 13:08:00 -0700773 public void setHeadsupDisappearRunning(boolean running) {
774 mHeadsupDisappearRunning = running;
775 mPrivateLayout.setHeadsupDisappearRunning(running);
776 }
777
Selim Cineke9bad242016-06-15 11:46:37 -0700778 public View getChildAfterViewWhenDismissed() {
779 return mChildAfterViewWhenDismissed;
780 }
781
782 public View getGroupParentWhenDismissed() {
783 return mGroupParentWhenDismissed;
784 }
785
Chris Wren78403d72014-07-28 10:23:24 +0100786 public interface ExpansionLogger {
787 public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
788 }
Selim Cinek1685e632014-04-08 02:27:49 +0200789
Chris Wren51c75102013-07-16 20:49:17 -0400790 public ExpandableNotificationRow(Context context, AttributeSet attrs) {
791 super(context, attrs);
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700792 mFalsingManager = FalsingManager.getInstance(context);
Selim Cinek01af3342016-02-09 19:25:31 -0800793 initDimens();
794 }
795
796 private void initDimens() {
Selim Cinekf619ffc2016-02-17 14:53:05 -0800797 mNotificationMinHeightLegacy = getFontScaledHeight(R.dimen.notification_min_height_legacy);
798 mNotificationMinHeight = getFontScaledHeight(R.dimen.notification_min_height);
799 mNotificationMaxHeight = getFontScaledHeight(R.dimen.notification_max_height);
800 mMaxHeadsUpHeightLegacy = getFontScaledHeight(
Selim Cinek77019c72015-12-09 10:18:02 -0800801 R.dimen.notification_max_heads_up_height_legacy);
Selim Cinekf619ffc2016-02-17 14:53:05 -0800802 mMaxHeadsUpHeight = getFontScaledHeight(R.dimen.notification_max_heads_up_height);
Mady Mellorb0a82462016-04-30 17:31:02 -0700803 mIncreasedPaddingBetweenElements = getResources()
804 .getDimensionPixelSize(R.dimen.notification_divider_height_increased);
Selim Cinekf619ffc2016-02-17 14:53:05 -0800805 }
806
807 /**
808 * @param dimenId the dimen to look up
809 * @return the font scaled dimen as if it were in sp but doesn't shrink sizes below dp
810 */
811 private int getFontScaledHeight(int dimenId) {
812 int dimensionPixelSize = getResources().getDimensionPixelSize(dimenId);
813 float factor = Math.max(1.0f, getResources().getDisplayMetrics().scaledDensity /
814 getResources().getDisplayMetrics().density);
815 return (int) (dimensionPixelSize * factor);
Chris Wren51c75102013-07-16 20:49:17 -0400816 }
817
Christoph Studera7fe6312014-06-27 19:32:44 +0200818 /**
819 * Resets this view so it can be re-used for an updated notification.
820 */
Christoph Studer22f2ee52014-07-29 22:57:21 +0200821 @Override
Christoph Studera7fe6312014-06-27 19:32:44 +0200822 public void reset() {
Christoph Studer22f2ee52014-07-29 22:57:21 +0200823 super.reset();
Chris Wren78403d72014-07-28 10:23:24 +0100824 final boolean wasExpanded = isExpanded();
Christoph Studera7fe6312014-06-27 19:32:44 +0200825 mExpandable = false;
826 mHasUserChangedExpansion = false;
827 mUserLocked = false;
828 mShowingPublic = false;
Jorim Jaggiae441282014-08-01 02:45:18 +0200829 mSensitive = false;
830 mShowingPublicInitialized = false;
Christoph Studera7fe6312014-06-27 19:32:44 +0200831 mIsSystemExpanded = false;
Selim Cinek83bc7832015-10-22 13:26:54 -0700832 mOnKeyguard = false;
Selim Cinek51d94912016-03-02 15:34:28 -0800833 mPublicLayout.reset();
834 mPrivateLayout.reset();
Selim Cinek31094df2014-08-14 19:28:15 +0200835 resetHeight();
Mady Mellor4b80b102016-01-22 08:03:58 -0800836 resetTranslation();
Selim Cinek31094df2014-08-14 19:28:15 +0200837 logExpansionEvent(false, wasExpanded);
838 }
839
840 public void resetHeight() {
Christoph Studera7fe6312014-06-27 19:32:44 +0200841 mMaxExpandHeight = 0;
Selim Cinek8d490d42015-04-10 00:05:50 -0700842 mHeadsUpHeight = 0;
Selim Cinek31094df2014-08-14 19:28:15 +0200843 onHeightReset();
Selim Cinek6e28a672014-09-05 14:43:28 +0200844 requestLayout();
Christoph Studera7fe6312014-06-27 19:32:44 +0200845 }
846
Jorim Jaggi251957d2014-04-09 04:24:09 +0200847 @Override
848 protected void onFinishInflate() {
849 super.onFinishInflate();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200850 mPublicLayout = (NotificationContentView) findViewById(R.id.expandedPublic);
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800851 mPublicLayout.setContainingNotification(this);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200852 mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded);
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800853 mPrivateLayout.setExpandClickListener(mExpandClickListener);
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800854 mPrivateLayout.setContainingNotification(this);
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800855 mPublicLayout.setExpandClickListener(mExpandClickListener);
Mady Mellor4b80b102016-01-22 08:03:58 -0800856 mSettingsIconRowStub = (ViewStub) findViewById(R.id.settings_icon_row_stub);
857 mSettingsIconRowStub.setOnInflateListener(new ViewStub.OnInflateListener() {
858 @Override
859 public void onInflate(ViewStub stub, View inflated) {
860 mSettingsIconRow = (NotificationSettingsIconRow) inflated;
861 mSettingsIconRow.setNotificationRowParent(ExpandableNotificationRow.this);
Mady Mellor3fd273e2016-03-15 21:08:14 -0700862 mSettingsIconRow.setAppName(mAppName);
Mady Mellor4b80b102016-01-22 08:03:58 -0800863 }
864 });
Selim Cinekab29aeb2015-02-20 18:18:32 +0100865 mGutsStub = (ViewStub) findViewById(R.id.notification_guts_stub);
866 mGutsStub.setOnInflateListener(new ViewStub.OnInflateListener() {
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200867 @Override
868 public void onInflate(ViewStub stub, View inflated) {
869 mGuts = (NotificationGuts) inflated;
870 mGuts.setClipTopAmount(getClipTopAmount());
871 mGuts.setActualHeight(getActualHeight());
Selim Cinekab29aeb2015-02-20 18:18:32 +0100872 mGutsStub = null;
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200873 }
874 });
Selim Cinekb5605e52015-02-20 18:21:41 +0100875 mChildrenContainerStub = (ViewStub) findViewById(R.id.child_container_stub);
876 mChildrenContainerStub.setOnInflateListener(new ViewStub.OnInflateListener() {
877
878 @Override
879 public void onInflate(ViewStub stub, View inflated) {
880 mChildrenContainer = (NotificationChildrenContainer) inflated;
Selim Cinek388df6d2015-10-22 13:25:11 -0700881 mChildrenContainer.setNotificationParent(ExpandableNotificationRow.this);
Selim Cinekc897bd32016-03-18 17:32:31 -0700882 mChildrenContainer.onNotificationUpdated();
Mady Mellor4b80b102016-01-22 08:03:58 -0800883 mTranslateableViews.add(mChildrenContainer);
Selim Cinekb5605e52015-02-20 18:21:41 +0100884 }
885 });
Selim Cinek863834b2014-05-20 04:20:25 +0200886 mVetoButton = findViewById(R.id.veto);
Mady Mellor4b80b102016-01-22 08:03:58 -0800887
888 // Add the views that we translate to reveal the gear
889 mTranslateableViews = new ArrayList<View>();
890 for (int i = 0; i < getChildCount(); i++) {
891 mTranslateableViews.add(getChildAt(i));
892 }
893 // Remove views that don't translate
894 mTranslateableViews.remove(mVetoButton);
895 mTranslateableViews.remove(mSettingsIconRowStub);
896 mTranslateableViews.remove(mChildrenContainerStub);
897 mTranslateableViews.remove(mGutsStub);
898 }
899
Mady Mellor4b80b102016-01-22 08:03:58 -0800900 public void resetTranslation() {
901 if (mTranslateableViews != null) {
902 for (int i = 0; i < mTranslateableViews.size(); i++) {
903 mTranslateableViews.get(i).setTranslationX(0);
904 }
Mady Mellor4b80b102016-01-22 08:03:58 -0800905 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700906 invalidateOutline();
Mady Mellor4b80b102016-01-22 08:03:58 -0800907 if (mSettingsIconRow != null) {
908 mSettingsIconRow.resetState();
909 }
910 }
911
912 public void animateTranslateNotification(final float leftTarget) {
913 if (mTranslateAnim != null) {
914 mTranslateAnim.cancel();
915 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700916 mTranslateAnim = getTranslateViewAnimator(leftTarget, null /* updateListener */);
Mady Mellor34958fa2016-02-23 09:52:17 -0800917 if (mTranslateAnim != null) {
918 mTranslateAnim.start();
919 }
920 }
921
922 @Override
923 public void setTranslation(float translationX) {
924 if (areGutsExposed()) {
925 // Don't translate if guts are showing.
926 return;
927 }
928 // Translate the group of views
929 for (int i = 0; i < mTranslateableViews.size(); i++) {
930 if (mTranslateableViews.get(i) != null) {
931 mTranslateableViews.get(i).setTranslationX(translationX);
932 }
933 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700934 invalidateOutline();
Mady Mellor34958fa2016-02-23 09:52:17 -0800935 if (mSettingsIconRow != null) {
936 mSettingsIconRow.updateSettingsIcons(translationX, getMeasuredWidth());
937 }
938 }
939
940 @Override
941 public float getTranslation() {
942 if (mTranslateableViews != null && mTranslateableViews.size() > 0) {
943 // All of the views in the list should have same translation, just use first one.
944 return mTranslateableViews.get(0).getTranslationX();
945 }
946 return 0;
947 }
948
949 public Animator getTranslateViewAnimator(final float leftTarget,
950 AnimatorUpdateListener listener) {
Mady Mellor723f1f92016-03-13 15:54:06 -0700951 if (mTranslateAnim != null) {
952 mTranslateAnim.cancel();
953 }
Mady Mellor34958fa2016-02-23 09:52:17 -0800954 if (areGutsExposed()) {
955 // No translation if guts are exposed.
956 return null;
957 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700958 final ObjectAnimator translateAnim = ObjectAnimator.ofFloat(this, TRANSLATE_CONTENT,
959 leftTarget);
960 if (listener != null) {
961 translateAnim.addUpdateListener(listener);
Mady Mellor4b80b102016-01-22 08:03:58 -0800962 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700963 translateAnim.addListener(new AnimatorListenerAdapter() {
964 boolean cancelled = false;
965
966 @Override
967 public void onAnimationCancel(Animator anim) {
968 cancelled = true;
969 }
970
971 @Override
972 public void onAnimationEnd(Animator anim) {
973 if (!cancelled && mSettingsIconRow != null && leftTarget == 0) {
974 mSettingsIconRow.resetState();
975 mTranslateAnim = null;
976 }
977 }
978 });
979 mTranslateAnim = translateAnim;
980 return translateAnim;
Mady Mellor4b80b102016-01-22 08:03:58 -0800981 }
982
983 public float getSpaceForGear() {
984 if (mSettingsIconRow != null) {
985 return mSettingsIconRow.getSpaceForGear();
986 }
987 return 0;
988 }
989
990 public NotificationSettingsIconRow getSettingsRow() {
991 if (mSettingsIconRow == null) {
992 mSettingsIconRowStub.inflate();
993 }
994 return mSettingsIconRow;
995 }
996
Selim Cinekab29aeb2015-02-20 18:18:32 +0100997 public void inflateGuts() {
998 if (mGuts == null) {
999 mGutsStub.inflate();
1000 }
1001 }
1002
Selim Cinekda42d652015-12-04 15:51:16 -08001003 private void updateChildrenVisibility() {
Selim Cinekd84a5932015-12-15 11:45:36 -08001004 mPrivateLayout.setVisibility(!mShowingPublic && !mIsSummaryWithChildren ? VISIBLE
1005 : INVISIBLE);
Selim Cinekef5127e2015-12-21 16:55:58 -08001006 if (mChildrenContainer != null) {
1007 mChildrenContainer.setVisibility(!mShowingPublic && mIsSummaryWithChildren ? VISIBLE
1008 : INVISIBLE);
Mady Mellorb0a82462016-04-30 17:31:02 -07001009 mChildrenContainer.updateHeaderVisibility(!mShowingPublic && mIsSummaryWithChildren
1010 ? VISIBLE
Selim Cinekef5127e2015-12-21 16:55:58 -08001011 : INVISIBLE);
1012 }
Selim Cinekda42d652015-12-04 15:51:16 -08001013 // The limits might have changed if the view suddenly became a group or vice versa
1014 updateLimits();
Selim Cinekb5605e52015-02-20 18:21:41 +01001015 }
1016
Jorim Jaggife40f7d2014-04-28 15:20:04 +02001017 @Override
Alan Viverettea54956a2015-01-07 16:05:02 -08001018 public boolean onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event) {
1019 if (super.onRequestSendAccessibilityEventInternal(child, event)) {
Jorim Jaggife40f7d2014-04-28 15:20:04 +02001020 // Add a record for the entire layout since its content is somehow small.
1021 // The event comes from a leaf view that is interacted with.
1022 AccessibilityEvent record = AccessibilityEvent.obtain();
1023 onInitializeAccessibilityEvent(record);
1024 dispatchPopulateAccessibilityEvent(record);
1025 event.appendRecord(record);
1026 return true;
1027 }
1028 return false;
Jorim Jaggic5dc0d02014-04-15 15:42:55 +02001029 }
Chris Wren51c75102013-07-16 20:49:17 -04001030
John Spurlocke15452b2014-08-21 09:44:39 -04001031 @Override
Jorim Jaggi4e857f42014-11-17 19:14:04 +01001032 public void setDark(boolean dark, boolean fade, long delay) {
1033 super.setDark(dark, fade, delay);
John Spurlocke15452b2014-08-21 09:44:39 -04001034 final NotificationContentView showing = getShowingLayout();
1035 if (showing != null) {
Jorim Jaggi4e857f42014-11-17 19:14:04 +01001036 showing.setDark(dark, fade, delay);
John Spurlocke15452b2014-08-21 09:44:39 -04001037 }
Selim Cinek9c7712d2015-12-08 19:19:48 -08001038 if (mIsSummaryWithChildren) {
Selim Cinekc897bd32016-03-18 17:32:31 -07001039 mChildrenContainer.setDark(dark, fade, delay);
Selim Cinek9c7712d2015-12-08 19:19:48 -08001040 }
John Spurlocke15452b2014-08-21 09:44:39 -04001041 }
1042
Chris Wren51c75102013-07-16 20:49:17 -04001043 public boolean isExpandable() {
Selim Cinek388df6d2015-10-22 13:25:11 -07001044 if (mIsSummaryWithChildren && !mShowingPublic) {
1045 return !mChildrenExpanded;
1046 }
Chris Wren51c75102013-07-16 20:49:17 -04001047 return mExpandable;
1048 }
1049
1050 public void setExpandable(boolean expandable) {
1051 mExpandable = expandable;
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001052 mPrivateLayout.updateExpandButtons(isExpandable());
Chris Wren51c75102013-07-16 20:49:17 -04001053 }
1054
Selim Cinek4ffd6362015-12-29 15:12:23 +01001055 @Override
1056 public void setClipToActualHeight(boolean clipToActualHeight) {
Selim Cinek084c16b2016-01-22 17:48:22 -08001057 super.setClipToActualHeight(clipToActualHeight || isUserLocked());
1058 getShowingLayout().setClipToActualHeight(clipToActualHeight || isUserLocked());
Selim Cinek4ffd6362015-12-29 15:12:23 +01001059 }
1060
Selim Cinek1685e632014-04-08 02:27:49 +02001061 /**
1062 * @return whether the user has changed the expansion state
1063 */
1064 public boolean hasUserChangedExpansion() {
1065 return mHasUserChangedExpansion;
1066 }
1067
Chris Wren51c75102013-07-16 20:49:17 -04001068 public boolean isUserExpanded() {
1069 return mUserExpanded;
1070 }
1071
Selim Cinek1685e632014-04-08 02:27:49 +02001072 /**
1073 * Set this notification to be expanded by the user
1074 *
1075 * @param userExpanded whether the user wants this notification to be expanded
1076 */
Chris Wren51c75102013-07-16 20:49:17 -04001077 public void setUserExpanded(boolean userExpanded) {
Selim Cinek388df6d2015-10-22 13:25:11 -07001078 setUserExpanded(userExpanded, false /* allowChildExpansion */);
1079 }
1080
1081 /**
1082 * Set this notification to be expanded by the user
1083 *
1084 * @param userExpanded whether the user wants this notification to be expanded
1085 * @param allowChildExpansion whether a call to this method allows expanding children
1086 */
1087 public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -07001088 mFalsingManager.setNotificationExpanded();
Selim Cinek388df6d2015-10-22 13:25:11 -07001089 if (mIsSummaryWithChildren && !mShowingPublic && allowChildExpansion) {
Chris Wren698b1702016-05-23 11:16:32 -04001090 final boolean wasExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
Selim Cinek388df6d2015-10-22 13:25:11 -07001091 mGroupManager.setGroupExpanded(mStatusBarNotification, userExpanded);
Chris Wren698b1702016-05-23 11:16:32 -04001092 logExpansionEvent(true /* userAction */, wasExpanded);
Selim Cinek388df6d2015-10-22 13:25:11 -07001093 return;
1094 }
Christoph Studera7fe6312014-06-27 19:32:44 +02001095 if (userExpanded && !mExpandable) return;
Chris Wren78403d72014-07-28 10:23:24 +01001096 final boolean wasExpanded = isExpanded();
Selim Cinek1685e632014-04-08 02:27:49 +02001097 mHasUserChangedExpansion = true;
Chris Wren51c75102013-07-16 20:49:17 -04001098 mUserExpanded = userExpanded;
Chris Wren78403d72014-07-28 10:23:24 +01001099 logExpansionEvent(true, wasExpanded);
Chris Wren51c75102013-07-16 20:49:17 -04001100 }
1101
Selim Cinekccd14fb2014-08-12 18:53:24 +02001102 public void resetUserExpansion() {
1103 mHasUserChangedExpansion = false;
1104 mUserExpanded = false;
1105 }
1106
Chris Wren51c75102013-07-16 20:49:17 -04001107 public boolean isUserLocked() {
Selim Cinek1b2a05e2016-04-28 14:20:39 -07001108 return mUserLocked && !mForceUnlocked;
Chris Wren51c75102013-07-16 20:49:17 -04001109 }
1110
1111 public void setUserLocked(boolean userLocked) {
1112 mUserLocked = userLocked;
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001113 mPrivateLayout.setUserExpanding(userLocked);
Selim Cinek42357e02016-02-24 18:48:01 -08001114 if (mIsSummaryWithChildren) {
1115 mChildrenContainer.setUserLocked(userLocked);
Mady Mellorc7d65b42016-05-04 11:44:57 -04001116 if (userLocked || (!userLocked && !isGroupExpanded())) {
Mady Mellorb0a82462016-04-30 17:31:02 -07001117 updateBackgroundForGroupState();
1118 }
Selim Cinek42357e02016-02-24 18:48:01 -08001119 }
Chris Wren51c75102013-07-16 20:49:17 -04001120 }
1121
Selim Cinek1685e632014-04-08 02:27:49 +02001122 /**
1123 * @return has the system set this notification to be expanded
1124 */
1125 public boolean isSystemExpanded() {
1126 return mIsSystemExpanded;
1127 }
1128
1129 /**
1130 * Set this notification to be expanded by the system.
1131 *
1132 * @param expand whether the system wants this notification to be expanded.
1133 */
1134 public void setSystemExpanded(boolean expand) {
Selim Cinek31094df2014-08-14 19:28:15 +02001135 if (expand != mIsSystemExpanded) {
1136 final boolean wasExpanded = isExpanded();
1137 mIsSystemExpanded = expand;
Selim Cinekb5605e52015-02-20 18:21:41 +01001138 notifyHeightChanged(false /* needsAnimation */);
Selim Cinek31094df2014-08-14 19:28:15 +02001139 logExpansionEvent(false, wasExpanded);
Selim Cineked6913b2016-06-01 12:01:17 -07001140 if (mIsSummaryWithChildren) {
Selim Cinekc897bd32016-03-18 17:32:31 -07001141 mChildrenContainer.updateGroupOverflow();
1142 }
Selim Cinek31094df2014-08-14 19:28:15 +02001143 }
Jorim Jaggidce3c4c2014-04-29 23:12:24 +02001144 }
1145
1146 /**
Selim Cinek83bc7832015-10-22 13:26:54 -07001147 * @param onKeyguard whether to prevent notification expansion
Jorim Jaggidce3c4c2014-04-29 23:12:24 +02001148 */
Selim Cinek83bc7832015-10-22 13:26:54 -07001149 public void setOnKeyguard(boolean onKeyguard) {
1150 if (onKeyguard != mOnKeyguard) {
Selim Cinek31094df2014-08-14 19:28:15 +02001151 final boolean wasExpanded = isExpanded();
Selim Cinek83bc7832015-10-22 13:26:54 -07001152 mOnKeyguard = onKeyguard;
Selim Cinek31094df2014-08-14 19:28:15 +02001153 logExpansionEvent(false, wasExpanded);
1154 if (wasExpanded != isExpanded()) {
Selim Cinekc897bd32016-03-18 17:32:31 -07001155 if (mIsSummaryWithChildren) {
1156 mChildrenContainer.updateGroupOverflow();
1157 }
Mady Mellor4b80b102016-01-22 08:03:58 -08001158 notifyHeightChanged(false /* needsAnimation */);
Selim Cinek31094df2014-08-14 19:28:15 +02001159 }
1160 }
Selim Cinek1685e632014-04-08 02:27:49 +02001161 }
1162
1163 /**
Dan Sandler0d3e62f2014-07-14 17:13:50 -04001164 * @return Can the underlying notification be cleared?
1165 */
1166 public boolean isClearable() {
Selim Cineka37774f2014-11-11 19:16:18 +01001167 return mStatusBarNotification != null && mStatusBarNotification.isClearable();
Dan Sandler0d3e62f2014-07-14 17:13:50 -04001168 }
1169
Jorim Jaggi9cbadd32014-05-01 20:18:31 +02001170 @Override
1171 public int getIntrinsicHeight() {
Jorim Jaggibe565df2014-04-28 17:51:23 +02001172 if (isUserLocked()) {
1173 return getActualHeight();
1174 }
Selim Cinekd84a5932015-12-15 11:45:36 -08001175 if (mGuts != null && mGuts.areGutsExposed()) {
1176 return mGuts.getHeight();
1177 } else if ((isChildInGroup() && !isGroupExpanded())) {
1178 return mPrivateLayout.getMinHeight();
1179 } else if (mSensitive && mHideSensitiveForIntrinsicHeight) {
1180 return getMinHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -07001181 } else if (mIsSummaryWithChildren && !mOnKeyguard) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001182 return mChildrenContainer.getIntrinsicHeight();
Selim Cinek73cf02a2016-06-17 13:08:00 -07001183 } else if (mIsHeadsUp || mHeadsupDisappearRunning) {
1184 if (isPinned() || mHeadsupDisappearRunning) {
Selim Cinek31aada42015-12-18 17:51:15 -08001185 return getPinnedHeadsUpHeight(true /* atLeastMinHeight */);
1186 } else if (isExpanded()) {
Selim Cinekd84a5932015-12-15 11:45:36 -08001187 return Math.max(getMaxExpandHeight(), mHeadsUpHeight);
Selim Cinek8d490d42015-04-10 00:05:50 -07001188 } else {
Selim Cinek567e8452016-03-24 10:54:56 -07001189 return Math.max(getCollapsedHeight(), mHeadsUpHeight);
Selim Cinek8d490d42015-04-10 00:05:50 -07001190 }
Selim Cinek31aada42015-12-18 17:51:15 -08001191 } else if (isExpanded()) {
Selim Cinek83bc7832015-10-22 13:26:54 -07001192 return getMaxExpandHeight();
Selim Cinekd84a5932015-12-15 11:45:36 -08001193 } else {
Selim Cinek567e8452016-03-24 10:54:56 -07001194 return getCollapsedHeight();
Selim Cinek1685e632014-04-08 02:27:49 +02001195 }
Selim Cinekb5605e52015-02-20 18:21:41 +01001196 }
Selim Cinek1685e632014-04-08 02:27:49 +02001197
Mady Mellorb0a82462016-04-30 17:31:02 -07001198 public boolean isGroupExpanded() {
Selim Cinekeef84282015-10-30 16:28:00 -07001199 return mGroupManager.isGroupExpanded(mStatusBarNotification);
Selim Cinekb5605e52015-02-20 18:21:41 +01001200 }
1201
Selim Cinek263398f2015-10-21 17:40:23 -07001202 private void onChildrenCountChanged() {
Selim Cinek8fc93c92015-11-23 17:48:07 -08001203 mIsSummaryWithChildren = BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS
Mady Mellorb0a82462016-04-30 17:31:02 -07001204 && mChildrenContainer != null && mChildrenContainer.getNotificationChildCount() > 0;
1205 if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() == null) {
1206 mChildrenContainer.recreateNotificationHeader(mExpandClickListener,
1207 mEntry.notification);
Selim Cinek263398f2015-10-21 17:40:23 -07001208 }
Mady Mellor6baed9e2016-05-25 16:05:09 -07001209 getShowingLayout().updateBackgroundColor(false /* animate */);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001210 mPrivateLayout.updateExpandButtons(isExpandable());
Selim Cinekea4bef72015-12-02 15:51:10 -08001211 updateChildrenHeaderAppearance();
Selim Cinekda42d652015-12-04 15:51:16 -08001212 updateChildrenVisibility();
Selim Cinek263398f2015-10-21 17:40:23 -07001213 }
1214
Mady Mellorb0a82462016-04-30 17:31:02 -07001215 public void updateChildrenHeaderAppearance() {
Selim Cineked6913b2016-06-01 12:01:17 -07001216 if (mIsSummaryWithChildren) {
Mady Mellorb0a82462016-04-30 17:31:02 -07001217 mChildrenContainer.updateChildrenHeaderAppearance();
1218 }
1219 }
1220
Selim Cinek1685e632014-04-08 02:27:49 +02001221 /**
1222 * Check whether the view state is currently expanded. This is given by the system in {@link
1223 * #setSystemExpanded(boolean)} and can be overridden by user expansion or
1224 * collapsing in {@link #setUserExpanded(boolean)}. Note that the visual appearance of this
1225 * view can differ from this state, if layout params are modified from outside.
1226 *
1227 * @return whether the view state is currently expanded.
1228 */
Selim Cinek83bc7832015-10-22 13:26:54 -07001229 public boolean isExpanded() {
Selim Cineke81b82b2016-03-04 11:22:28 -08001230 return isExpanded(false /* allowOnKeyguard */);
1231 }
1232
1233 public boolean isExpanded(boolean allowOnKeyguard) {
1234 return (!mOnKeyguard || allowOnKeyguard)
Selim Cinekb5605e52015-02-20 18:21:41 +01001235 && (!hasUserChangedExpansion() && (isSystemExpanded() || isSystemChildExpanded())
1236 || isUserExpanded());
1237 }
1238
1239 private boolean isSystemChildExpanded() {
1240 return mIsSystemChildExpanded;
1241 }
1242
1243 public void setSystemChildExpanded(boolean expanded) {
1244 mIsSystemChildExpanded = expanded;
Selim Cinek1685e632014-04-08 02:27:49 +02001245 }
1246
1247 @Override
1248 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
1249 super.onLayout(changed, left, top, right, bottom);
Selim Cinek8d490d42015-04-10 00:05:50 -07001250 updateMaxHeights();
Mady Mellora6edc872016-04-26 11:01:03 -07001251 if (mSettingsIconRow != null) {
1252 mSettingsIconRow.updateVerticalLocation();
1253 }
Selim Cinek1685e632014-04-08 02:27:49 +02001254 }
1255
Selim Cinek8d490d42015-04-10 00:05:50 -07001256 private void updateMaxHeights() {
Selim Cinekd2319fb2014-09-01 19:41:54 +02001257 int intrinsicBefore = getIntrinsicHeight();
Selim Cinek8d490d42015-04-10 00:05:50 -07001258 View expandedChild = mPrivateLayout.getExpandedChild();
1259 if (expandedChild == null) {
1260 expandedChild = mPrivateLayout.getContractedChild();
1261 }
1262 mMaxExpandHeight = expandedChild.getHeight();
1263 View headsUpChild = mPrivateLayout.getHeadsUpChild();
Selim Cinek1f3f5442015-04-10 17:54:46 -07001264 if (headsUpChild == null) {
1265 headsUpChild = mPrivateLayout.getContractedChild();
Selim Cinek8d490d42015-04-10 00:05:50 -07001266 }
Selim Cinek1f3f5442015-04-10 17:54:46 -07001267 mHeadsUpHeight = headsUpChild.getHeight();
Selim Cinekd2319fb2014-09-01 19:41:54 +02001268 if (intrinsicBefore != getIntrinsicHeight()) {
Selim Cinekb5605e52015-02-20 18:21:41 +01001269 notifyHeightChanged(false /* needsAnimation */);
Selim Cinekd2319fb2014-09-01 19:41:54 +02001270 }
1271 }
1272
Selim Cinekfa0a2d32016-01-14 13:02:21 -08001273 @Override
1274 public void notifyHeightChanged(boolean needsAnimation) {
1275 super.notifyHeightChanged(needsAnimation);
1276 getShowingLayout().requestSelectLayout(needsAnimation || isUserLocked());
1277 }
1278
Selim Cinek3c76d502016-02-19 15:16:33 -08001279 public void setSensitive(boolean sensitive, boolean hideSensitive) {
Jorim Jaggiae441282014-08-01 02:45:18 +02001280 mSensitive = sensitive;
Selim Cinek3c76d502016-02-19 15:16:33 -08001281 mSensitiveHiddenInGeneral = hideSensitive;
Jorim Jaggiae441282014-08-01 02:45:18 +02001282 }
1283
1284 public void setHideSensitiveForIntrinsicHeight(boolean hideSensitive) {
Selim Cinek60122be2015-04-15 18:16:50 -07001285 mHideSensitiveForIntrinsicHeight = hideSensitive;
Selim Cineka52f6a12016-02-29 15:35:58 -08001286 if (mIsSummaryWithChildren) {
1287 List<ExpandableNotificationRow> notificationChildren =
1288 mChildrenContainer.getNotificationChildren();
1289 for (int i = 0; i < notificationChildren.size(); i++) {
1290 ExpandableNotificationRow child = notificationChildren.get(i);
1291 child.setHideSensitiveForIntrinsicHeight(hideSensitive);
1292 }
1293 }
Jorim Jaggiae441282014-08-01 02:45:18 +02001294 }
1295
1296 public void setHideSensitive(boolean hideSensitive, boolean animated, long delay,
1297 long duration) {
1298 boolean oldShowingPublic = mShowingPublic;
1299 mShowingPublic = mSensitive && hideSensitive;
1300 if (mShowingPublicInitialized && mShowingPublic == oldShowingPublic) {
1301 return;
1302 }
Dan Sandlera5e0f412014-01-23 15:11:54 -05001303
1304 // bail out if no public version
Selim Cinek1685e632014-04-08 02:27:49 +02001305 if (mPublicLayout.getChildCount() == 0) return;
Dan Sandlera5e0f412014-01-23 15:11:54 -05001306
Jorim Jaggiae441282014-08-01 02:45:18 +02001307 if (!animated) {
1308 mPublicLayout.animate().cancel();
1309 mPrivateLayout.animate().cancel();
Selim Cineka554c702016-06-17 18:02:12 -07001310 if (mChildrenContainer != null) {
1311 mChildrenContainer.animate().cancel();
1312 mChildrenContainer.setAlpha(1f);
1313 }
Jorim Jaggiae441282014-08-01 02:45:18 +02001314 mPublicLayout.setAlpha(1f);
1315 mPrivateLayout.setAlpha(1f);
1316 mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE);
Selim Cinekd84a5932015-12-15 11:45:36 -08001317 updateChildrenVisibility();
Jorim Jaggiae441282014-08-01 02:45:18 +02001318 } else {
1319 animateShowingPublic(delay, duration);
1320 }
Selim Cinekc3179332016-03-04 14:44:56 -08001321 NotificationContentView showingLayout = getShowingLayout();
1322 showingLayout.updateBackgroundColor(animated);
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001323 mPrivateLayout.updateExpandButtons(isExpandable());
Selim Cinek757d8792016-01-28 16:21:08 -08001324 updateClearability();
Jorim Jaggiae441282014-08-01 02:45:18 +02001325 mShowingPublicInitialized = true;
1326 }
1327
1328 private void animateShowingPublic(long delay, long duration) {
Mady Mellorb0a82462016-04-30 17:31:02 -07001329 View[] privateViews = mIsSummaryWithChildren
1330 ? new View[] {mChildrenContainer}
Selim Cinekd84a5932015-12-15 11:45:36 -08001331 : new View[] {mPrivateLayout};
1332 View[] publicViews = new View[] {mPublicLayout};
1333 View[] hiddenChildren = mShowingPublic ? privateViews : publicViews;
1334 View[] shownChildren = mShowingPublic ? publicViews : privateViews;
1335 for (final View hiddenView : hiddenChildren) {
1336 hiddenView.setVisibility(View.VISIBLE);
1337 hiddenView.animate().cancel();
1338 hiddenView.animate()
1339 .alpha(0f)
1340 .setStartDelay(delay)
1341 .setDuration(duration)
1342 .withEndAction(new Runnable() {
1343 @Override
1344 public void run() {
1345 hiddenView.setVisibility(View.INVISIBLE);
1346 }
1347 });
1348 }
1349 for (View showView : shownChildren) {
1350 showView.setVisibility(View.VISIBLE);
1351 showView.setAlpha(0f);
1352 showView.animate().cancel();
1353 showView.animate()
1354 .alpha(1f)
1355 .setStartDelay(delay)
1356 .setDuration(duration);
1357 }
Dan Sandler0d3e62f2014-07-14 17:13:50 -04001358 }
1359
Selim Cinek3776fe02016-02-04 13:32:43 -08001360 public boolean mustStayOnScreen() {
1361 return mIsHeadsUp;
1362 }
1363
Selim Cinek757d8792016-01-28 16:21:08 -08001364 private void updateClearability() {
Dan Sandler0d3e62f2014-07-14 17:13:50 -04001365 // public versions cannot be dismissed
Selim Cineke9bad242016-06-15 11:46:37 -07001366 mVetoButton.setVisibility(canViewBeDismissed() ? View.VISIBLE : View.GONE);
1367 }
1368
1369 private boolean canViewBeDismissed() {
1370 return isClearable() && (!mShowingPublic || !mSensitiveHiddenInGeneral);
Dan Sandlera5e0f412014-01-23 15:11:54 -05001371 }
Jorim Jaggi251957d2014-04-09 04:24:09 +02001372
Ricky Waicd35def2016-05-03 11:07:07 +01001373 public void makeActionsVisibile() {
1374 setUserExpanded(true, true);
1375 if (isChildInGroup()) {
1376 mGroupManager.setGroupExpanded(mStatusBarNotification, true);
1377 }
1378 notifyHeightChanged(false);
1379 }
1380
Selim Cinekb5605e52015-02-20 18:21:41 +01001381 public void setChildrenExpanded(boolean expanded, boolean animate) {
1382 mChildrenExpanded = expanded;
Selim Cinek83bc7832015-10-22 13:26:54 -07001383 if (mChildrenContainer != null) {
1384 mChildrenContainer.setChildrenExpanded(expanded);
1385 }
Selim Cinekddf1b392016-05-27 16:33:10 -07001386 updateClickAndFocus();
Selim Cinekb5605e52015-02-20 18:21:41 +01001387 }
1388
Selim Cinekb5605e52015-02-20 18:21:41 +01001389 public static void applyTint(View v, int color) {
1390 int alpha;
1391 if (color != 0) {
1392 alpha = COLORED_DIVIDER_ALPHA;
1393 } else {
1394 color = 0xff000000;
1395 alpha = DEFAULT_DIVIDER_ALPHA;
1396 }
1397 if (v.getBackground() instanceof ColorDrawable) {
1398 ColorDrawable background = (ColorDrawable) v.getBackground();
1399 background.mutate();
1400 background.setColor(color);
1401 background.setAlpha(alpha);
1402 }
1403 }
1404
Selim Cinek1685e632014-04-08 02:27:49 +02001405 public int getMaxExpandHeight() {
Selim Cinekb5605e52015-02-20 18:21:41 +01001406 return mMaxExpandHeight;
Chris Wren51c75102013-07-16 20:49:17 -04001407 }
Jorim Jaggi584a7aa2014-04-10 23:26:13 +02001408
Mady Mellor34958fa2016-02-23 09:52:17 -08001409 public boolean areGutsExposed() {
1410 return (mGuts != null && mGuts.areGutsExposed());
1411 }
1412
Jorim Jaggibe565df2014-04-28 17:51:23 +02001413 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001414 public boolean isContentExpandable() {
Selim Cinek2f0df8a2014-06-10 17:40:42 +02001415 NotificationContentView showingLayout = getShowingLayout();
1416 return showingLayout.isContentExpandable();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001417 }
1418
1419 @Override
Selim Cinek560e64d2015-06-09 19:58:11 -07001420 protected View getContentView() {
Selim Cineka5703182016-05-11 21:23:16 -04001421 if (mIsSummaryWithChildren) {
1422 return mChildrenContainer;
1423 }
Selim Cinek560e64d2015-06-09 19:58:11 -07001424 return getShowingLayout();
1425 }
1426
1427 @Override
Mady Mellorb0a82462016-04-30 17:31:02 -07001428 public int getExtraBottomPadding() {
1429 if (mIsSummaryWithChildren && isGroupExpanded()) {
1430 return mIncreasedPaddingBetweenElements;
1431 }
1432 return 0;
1433 }
1434
1435 @Override
Jorim Jaggid552d9d2014-05-07 19:41:13 +02001436 public void setActualHeight(int height, boolean notifyListeners) {
Selim Cinekb5605e52015-02-20 18:21:41 +01001437 super.setActualHeight(height, notifyListeners);
Mady Mellorb53bc272016-02-11 18:28:23 -08001438 if (mGuts != null && mGuts.areGutsExposed()) {
1439 mGuts.setActualHeight(height);
1440 return;
1441 }
Selim Cinekeef84282015-10-30 16:28:00 -07001442 int contentHeight = Math.max(getMinHeight(), height);
Selim Cinekb5605e52015-02-20 18:21:41 +01001443 mPrivateLayout.setContentHeight(contentHeight);
1444 mPublicLayout.setContentHeight(contentHeight);
Selim Cinek42357e02016-02-24 18:48:01 -08001445 if (mIsSummaryWithChildren) {
1446 mChildrenContainer.setActualHeight(height);
1447 }
Jorim Jaggib1cd3c12014-09-08 19:55:17 +02001448 if (mGuts != null) {
1449 mGuts.setActualHeight(height);
1450 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001451 }
1452
1453 @Override
Selim Cinekb5605e52015-02-20 18:21:41 +01001454 public int getMaxContentHeight() {
Selim Cinek83bc7832015-10-22 13:26:54 -07001455 if (mIsSummaryWithChildren && !mShowingPublic) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001456 return mChildrenContainer.getMaxContentHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -07001457 }
Selim Cinek2f0df8a2014-06-10 17:40:42 +02001458 NotificationContentView showingLayout = getShowingLayout();
1459 return showingLayout.getMaxHeight();
Jorim Jaggibe565df2014-04-28 17:51:23 +02001460 }
1461
1462 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001463 public int getMinHeight() {
Selim Cinek31aada42015-12-18 17:51:15 -08001464 if (mIsHeadsUp && mHeadsUpManager.isTrackingHeadsUp()) {
1465 return getPinnedHeadsUpHeight(false /* atLeastMinHeight */);
1466 } else if (mIsSummaryWithChildren && !isGroupExpanded() && !mShowingPublic) {
Selim Cinekb55386d2015-12-16 17:26:49 -08001467 return mChildrenContainer.getMinHeight();
Selim Cinek31aada42015-12-18 17:51:15 -08001468 } else if (mIsHeadsUp) {
1469 return mHeadsUpHeight;
Selim Cinekb55386d2015-12-16 17:26:49 -08001470 }
Selim Cinek816c8e42015-11-19 12:00:45 -08001471 NotificationContentView showingLayout = getShowingLayout();
1472 return showingLayout.getMinHeight();
1473 }
1474
1475 @Override
Selim Cinek567e8452016-03-24 10:54:56 -07001476 public int getCollapsedHeight() {
Selim Cinek2c584612016-02-29 16:14:25 -08001477 if (mIsSummaryWithChildren && !mShowingPublic) {
Selim Cinek567e8452016-03-24 10:54:56 -07001478 return mChildrenContainer.getCollapsedHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -07001479 }
Selim Cinek816c8e42015-11-19 12:00:45 -08001480 return getMinHeight();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001481 }
1482
1483 @Override
Jorim Jaggibe565df2014-04-28 17:51:23 +02001484 public void setClipTopAmount(int clipTopAmount) {
1485 super.setClipTopAmount(clipTopAmount);
1486 mPrivateLayout.setClipTopAmount(clipTopAmount);
Selim Cinek2f0df8a2014-06-10 17:40:42 +02001487 mPublicLayout.setClipTopAmount(clipTopAmount);
Jorim Jaggib1cd3c12014-09-08 19:55:17 +02001488 if (mGuts != null) {
1489 mGuts.setClipTopAmount(clipTopAmount);
1490 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001491 }
1492
Selim Cinek31094df2014-08-14 19:28:15 +02001493 public boolean isMaxExpandHeightInitialized() {
1494 return mMaxExpandHeight != 0;
Selim Cinek7d447722014-06-10 15:51:59 +02001495 }
Selim Cinek2f0df8a2014-06-10 17:40:42 +02001496
Selim Cinek42357e02016-02-24 18:48:01 -08001497 public NotificationContentView getShowingLayout() {
Selim Cinek2f0df8a2014-06-10 17:40:42 +02001498 return mShowingPublic ? mPublicLayout : mPrivateLayout;
1499 }
Chris Wren78403d72014-07-28 10:23:24 +01001500
Jorim Jaggi59ec3042015-06-05 15:18:43 -07001501 @Override
1502 public void setShowingLegacyBackground(boolean showing) {
1503 super.setShowingLegacyBackground(showing);
1504 mPrivateLayout.setShowingLegacyBackground(showing);
1505 mPublicLayout.setShowingLegacyBackground(showing);
1506 }
1507
Selim Cineka6c6bfb2015-10-29 16:27:08 -07001508 @Override
1509 protected void updateBackgroundTint() {
1510 super.updateBackgroundTint();
Mady Mellorb0a82462016-04-30 17:31:02 -07001511 updateBackgroundForGroupState();
Selim Cineka6c6bfb2015-10-29 16:27:08 -07001512 if (mIsSummaryWithChildren) {
1513 List<ExpandableNotificationRow> notificationChildren =
1514 mChildrenContainer.getNotificationChildren();
1515 for (int i = 0; i < notificationChildren.size(); i++) {
1516 ExpandableNotificationRow child = notificationChildren.get(i);
Mady Mellorb0a82462016-04-30 17:31:02 -07001517 child.updateBackgroundForGroupState();
Selim Cineka6c6bfb2015-10-29 16:27:08 -07001518 }
1519 }
1520 }
1521
Mady Mellorb0a82462016-04-30 17:31:02 -07001522 /**
1523 * Called when a group has finished animating from collapsed or expanded state.
1524 */
1525 public void onFinishedExpansionChange() {
1526 mGroupExpansionChanging = false;
1527 updateBackgroundForGroupState();
1528 }
1529
1530 /**
1531 * Updates the parent and children backgrounds in a group based on the expansion state.
1532 */
1533 public void updateBackgroundForGroupState() {
1534 if (mIsSummaryWithChildren) {
1535 // Only when the group has finished expanding do we hide its background.
1536 mShowNoBackground = isGroupExpanded() && !isGroupExpansionChanging() && !isUserLocked();
1537 mChildrenContainer.updateHeaderForExpansion(mShowNoBackground);
1538 List<ExpandableNotificationRow> children = mChildrenContainer.getNotificationChildren();
1539 for (int i = 0; i < children.size(); i++) {
1540 children.get(i).updateBackgroundForGroupState();
1541 }
1542 } else if (isChildInGroup()) {
1543 final int childColor = getShowingLayout().getBackgroundColorForExpansionState();
1544 // Only show a background if the group is expanded OR if it is expanding / collapsing
1545 // and has a custom background color
1546 final boolean showBackground = isGroupExpanded()
1547 || ((mNotificationParent.isGroupExpansionChanging()
1548 || mNotificationParent.isUserLocked()) && childColor != 0);
1549 mShowNoBackground = !showBackground;
1550 } else {
1551 // Only children or parents ever need no background.
1552 mShowNoBackground = false;
1553 }
1554 updateOutline();
Selim Cineka6c6bfb2015-10-29 16:27:08 -07001555 updateBackground();
1556 }
1557
Adrian Roos4a579672016-05-24 16:54:37 -07001558 public int getPositionOfChild(ExpandableNotificationRow childRow) {
1559 if (mIsSummaryWithChildren) {
1560 return mChildrenContainer.getPositionInLinearLayout(childRow);
1561 }
1562 return 0;
1563 }
1564
Chris Wren78403d72014-07-28 10:23:24 +01001565 public void setExpansionLogger(ExpansionLogger logger, String key) {
1566 mLogger = logger;
1567 mLoggingKey = key;
1568 }
1569
Chris Wren6abeeb92016-05-26 14:44:38 -04001570 public void onExpandedByGesture(boolean userExpanded) {
1571 int event = MetricsEvent.ACTION_NOTIFICATION_GESTURE_EXPANDER;
1572 if (mGroupManager.isSummaryOfGroup(getStatusBarNotification())) {
1573 event = MetricsEvent.ACTION_NOTIFICATION_GROUP_GESTURE_EXPANDER;
1574 }
1575 MetricsLogger.action(mContext, event, userExpanded);
1576 }
1577
Selim Cinek6183d122016-01-14 18:48:41 -08001578 @Override
Selim Cinek42357e02016-02-24 18:48:01 -08001579 public float getIncreasedPaddingAmount() {
1580 if (mIsSummaryWithChildren) {
1581 if (isGroupExpanded()) {
1582 return 1.0f;
1583 } else if (isUserLocked()) {
Selim Cinekf07d0622016-03-21 19:52:52 -07001584 return mChildrenContainer.getGroupExpandFraction();
Selim Cinek42357e02016-02-24 18:48:01 -08001585 }
1586 }
1587 return 0.0f;
Selim Cinek61633a82016-01-25 15:54:10 -08001588 }
1589
1590 @Override
Selim Cinek6183d122016-01-14 18:48:41 -08001591 protected boolean disallowSingleClick(MotionEvent event) {
1592 float x = event.getX();
1593 float y = event.getY();
Selim Cinek34eda5e2016-02-18 17:10:43 -08001594 NotificationHeaderView header = getVisibleNotificationHeader();
Selim Cinek6183d122016-01-14 18:48:41 -08001595 if (header != null) {
Mady Mellora8833512016-03-09 09:50:18 -08001596 return header.isInTouchRect(x - getTranslation(), y);
Selim Cinek6183d122016-01-14 18:48:41 -08001597 }
1598 return super.disallowSingleClick(event);
1599 }
1600
Chris Wren78403d72014-07-28 10:23:24 +01001601 private void logExpansionEvent(boolean userAction, boolean wasExpanded) {
Chris Wren698b1702016-05-23 11:16:32 -04001602 boolean nowExpanded = isExpanded();
1603 if (mIsSummaryWithChildren) {
1604 nowExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
1605 }
Chris Wren78403d72014-07-28 10:23:24 +01001606 if (wasExpanded != nowExpanded && mLogger != null) {
1607 mLogger.logNotificationExpansion(mLoggingKey, userAction, nowExpanded) ;
1608 }
1609 }
Selim Cinek570981d2015-12-01 11:37:01 -08001610
Selim Cineke9bad242016-06-15 11:46:37 -07001611 @Override
1612 public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
1613 super.onInitializeAccessibilityNodeInfoInternal(info);
1614 if (canViewBeDismissed()) {
1615 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS);
1616 }
1617 }
1618
1619 @Override
1620 public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
1621 if (super.performAccessibilityActionInternal(action, arguments)) {
1622 return true;
1623 }
1624 switch (action) {
1625 case AccessibilityNodeInfo.ACTION_DISMISS:
1626 NotificationStackScrollLayout.performDismiss(this, mGroupManager,
1627 true /* fromAccessibility */);
1628 return true;
1629 }
1630 return false;
1631 }
1632
1633 public boolean shouldRefocusOnDismiss() {
1634 return mRefocusOnDismiss || isAccessibilityFocused();
1635 }
1636
Selim Cinek570981d2015-12-01 11:37:01 -08001637 public interface OnExpandClickListener {
Selim Cinek31aada42015-12-18 17:51:15 -08001638 void onExpandClicked(NotificationData.Entry clickedEntry, boolean nowExpanded);
Selim Cinek570981d2015-12-01 11:37:01 -08001639 }
Chris Wren51c75102013-07-16 20:49:17 -04001640}