blob: 6632ae63d94425a161e83abdb0e063940668e11f [file] [log] [blame]
Selim Cinekb5605e52015-02-20 18:21:41 +01001/*
2 * Copyright (C) 2015 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
Rohan Shah20790b82018-07-02 17:21:04 -070017package com.android.systemui.statusbar.notification.stack;
Selim Cinekb5605e52015-02-20 18:21:41 +010018
Mady Mellorb0a82462016-04-30 17:31:02 -070019import android.app.Notification;
Selim Cinekb5605e52015-02-20 18:21:41 +010020import android.content.Context;
Selim Cineka3d3b912016-02-02 11:22:06 -080021import android.content.res.Configuration;
Anthony Chen6bf88a02017-04-10 14:41:44 -070022import android.content.res.Resources;
Mady Mellorb0a82462016-04-30 17:31:02 -070023import android.graphics.drawable.ColorDrawable;
24import android.service.notification.StatusBarNotification;
Selim Cinekb5605e52015-02-20 18:21:41 +010025import android.util.AttributeSet;
26import android.view.LayoutInflater;
Mady Mellorb0a82462016-04-30 17:31:02 -070027import android.view.NotificationHeaderView;
Selim Cinekb5605e52015-02-20 18:21:41 +010028import android.view.View;
29import android.view.ViewGroup;
Mady Mellorb0a82462016-04-30 17:31:02 -070030import android.widget.RemoteViews;
Selim Cinekc897bd32016-03-18 17:32:31 -070031import android.widget.TextView;
Selim Cinek817abe72017-05-24 11:08:55 -070032
33import com.android.internal.annotations.VisibleForTesting;
Selim Cinekb5605e52015-02-20 18:21:41 +010034import com.android.systemui.R;
Selim Cinek7b73a4c2016-01-12 18:32:11 -080035import com.android.systemui.statusbar.CrossFadeHelper;
Mady Mellorb0a82462016-04-30 17:31:02 -070036import com.android.systemui.statusbar.NotificationHeaderUtil;
Gus Prevasab336792018-11-14 13:52:20 -050037import com.android.systemui.statusbar.notification.NotificationUtils;
38import com.android.systemui.statusbar.notification.VisualStabilityManager;
39import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
Rohan Shah20790b82018-07-02 17:21:04 -070040import com.android.systemui.statusbar.notification.row.HybridGroupManager;
41import com.android.systemui.statusbar.notification.row.HybridNotificationView;
Rohan Shah20790b82018-07-02 17:21:04 -070042import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
Selim Cinekb5605e52015-02-20 18:21:41 +010043
44import java.util.ArrayList;
45import java.util.List;
46
47/**
48 * A container containing child notifications
49 */
50public class NotificationChildrenContainer extends ViewGroup {
51
Aaron Heuckroth17ce40e2018-06-19 15:44:40 -040052 @VisibleForTesting
53 static final int NUMBER_OF_CHILDREN_WHEN_COLLAPSED = 2;
54 @VisibleForTesting
55 static final int NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED = 5;
56 @VisibleForTesting
57 static final int NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED = 8;
Selim Cinek414ad332017-02-24 19:06:12 -080058 private static final AnimationProperties ALPHA_FADE_IN = new AnimationProperties() {
59 private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha();
60
61 @Override
62 public AnimationFilter getAnimationFilter() {
63 return mAnimationFilter;
64 }
65 }.setDuration(200);
Selim Cinek83bc7832015-10-22 13:26:54 -070066
Selim Cinekb5605e52015-02-20 18:21:41 +010067 private final List<View> mDividers = new ArrayList<>();
68 private final List<ExpandableNotificationRow> mChildren = new ArrayList<>();
Selim Cinekc897bd32016-03-18 17:32:31 -070069 private final HybridGroupManager mHybridGroupManager;
Selim Cinek01af3342016-02-09 19:25:31 -080070 private int mChildPadding;
71 private int mDividerHeight;
Anthony Chen6bf88a02017-04-10 14:41:44 -070072 private float mDividerAlpha;
Mady Mellorb0a82462016-04-30 17:31:02 -070073 private int mNotificationHeaderMargin;
Anthony Chen6bf88a02017-04-10 14:41:44 -070074
Selim Cinek01af3342016-02-09 19:25:31 -080075 private int mNotificatonTopPadding;
76 private float mCollapsedBottompadding;
Selim Cinek83bc7832015-10-22 13:26:54 -070077 private boolean mChildrenExpanded;
Selim Cinek414ad332017-02-24 19:06:12 -080078 private ExpandableNotificationRow mContainingNotification;
Selim Cinekc897bd32016-03-18 17:32:31 -070079 private TextView mOverflowNumber;
80 private ViewState mGroupOverFlowState;
Selim Cineka69f2a62015-12-11 17:28:12 -080081 private int mRealHeight;
Selim Cinek42357e02016-02-24 18:48:01 -080082 private boolean mUserLocked;
83 private int mActualHeight;
Selim Cinekc897bd32016-03-18 17:32:31 -070084 private boolean mNeverAppliedGroupState;
Mady Mellorb0a82462016-04-30 17:31:02 -070085 private int mHeaderHeight;
86
Anthony Chen6bf88a02017-04-10 14:41:44 -070087 /**
88 * Whether or not individual notifications that are part of this container will have shadows.
89 */
90 private boolean mEnableShadowOnChildNotifications;
91
Mady Mellorb0a82462016-04-30 17:31:02 -070092 private NotificationHeaderView mNotificationHeader;
93 private NotificationViewWrapper mNotificationHeaderWrapper;
Selim Cinek414ad332017-02-24 19:06:12 -080094 private NotificationHeaderView mNotificationHeaderLowPriority;
95 private NotificationViewWrapper mNotificationHeaderWrapperLowPriority;
Mady Mellorb0a82462016-04-30 17:31:02 -070096 private NotificationHeaderUtil mHeaderUtil;
97 private ViewState mHeaderViewState;
Selim Cinekb3dadcc2016-11-21 17:21:13 -080098 private int mClipBottomAmount;
Selim Cinek6743c0b2017-01-18 18:24:01 -080099 private boolean mIsLowPriority;
Selim Cinek414ad332017-02-24 19:06:12 -0800100 private OnClickListener mHeaderClickListener;
Adrian Roos6f6e1592017-05-02 16:22:53 -0700101 private ViewGroup mCurrentHeader;
Selim Cinekb5605e52015-02-20 18:21:41 +0100102
Anthony Chen6bf88a02017-04-10 14:41:44 -0700103 private boolean mShowDividersWhenExpanded;
104 private boolean mHideDividersDuringExpand;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800105 private int mTranslationForHeader;
106 private int mCurrentHeaderTranslation = 0;
107 private float mHeaderVisibleAmount = 1.0f;
Anthony Chen6bf88a02017-04-10 14:41:44 -0700108
Selim Cinekb5605e52015-02-20 18:21:41 +0100109 public NotificationChildrenContainer(Context context) {
110 this(context, null);
111 }
112
113 public NotificationChildrenContainer(Context context, AttributeSet attrs) {
114 this(context, attrs, 0);
115 }
116
117 public NotificationChildrenContainer(Context context, AttributeSet attrs, int defStyleAttr) {
118 this(context, attrs, defStyleAttr, 0);
119 }
120
121 public NotificationChildrenContainer(Context context, AttributeSet attrs, int defStyleAttr,
122 int defStyleRes) {
123 super(context, attrs, defStyleAttr, defStyleRes);
Selim Cinekc897bd32016-03-18 17:32:31 -0700124 mHybridGroupManager = new HybridGroupManager(getContext(), this);
dongwan0605.kim11e7dec2018-05-06 18:04:52 +0900125 initDimens();
Selim Cineked64a142018-02-06 18:06:01 -0800126 setClipChildren(false);
Selim Cinek01af3342016-02-09 19:25:31 -0800127 }
128
129 private void initDimens() {
Anthony Chen6bf88a02017-04-10 14:41:44 -0700130 Resources res = getResources();
131 mChildPadding = res.getDimensionPixelSize(R.dimen.notification_children_padding);
132 mDividerHeight = res.getDimensionPixelSize(
133 R.dimen.notification_children_container_divider_height);
134 mDividerAlpha = res.getFloat(R.dimen.notification_divider_alpha);
Anthony Chen6bf88a02017-04-10 14:41:44 -0700135 mNotificationHeaderMargin = res.getDimensionPixelSize(
136 R.dimen.notification_children_container_margin_top);
137 mNotificatonTopPadding = res.getDimensionPixelSize(
Selim Cinek7b836392015-12-04 20:02:59 -0800138 R.dimen.notification_children_container_top_padding);
Selim Cinekafeed292017-12-12 17:32:44 -0800139 mHeaderHeight = mNotificationHeaderMargin + mNotificatonTopPadding;
Anthony Chen6bf88a02017-04-10 14:41:44 -0700140 mCollapsedBottompadding = res.getDimensionPixelSize(
Selim Cineked64a142018-02-06 18:06:01 -0800141 com.android.internal.R.dimen.notification_content_margin);
Anthony Chen6bf88a02017-04-10 14:41:44 -0700142 mEnableShadowOnChildNotifications =
143 res.getBoolean(R.bool.config_enableShadowOnChildNotifications);
144 mShowDividersWhenExpanded =
145 res.getBoolean(R.bool.config_showDividersWhenGroupNotificationExpanded);
146 mHideDividersDuringExpand =
147 res.getBoolean(R.bool.config_hideDividersDuringExpand);
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800148 mTranslationForHeader = res.getDimensionPixelSize(
149 com.android.internal.R.dimen.notification_content_margin)
150 - mNotificationHeaderMargin;
dongwan0605.kim11e7dec2018-05-06 18:04:52 +0900151 mHybridGroupManager.initDimens();
Selim Cinekb5605e52015-02-20 18:21:41 +0100152 }
153
154 @Override
155 protected void onLayout(boolean changed, int l, int t, int r, int b) {
Selim Cinek471e31a2015-12-11 13:39:48 -0800156 int childCount = Math.min(mChildren.size(), NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
Selim Cinekb5605e52015-02-20 18:21:41 +0100157 for (int i = 0; i < childCount; i++) {
158 View child = mChildren.get(i);
Selim Cinekfa760d42016-05-10 15:50:53 -0400159 // We need to layout all children even the GONE ones, such that the heights are
160 // calculated correctly as they are used to calculate how many we can fit on the screen
Selim Cinekc897bd32016-03-18 17:32:31 -0700161 child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
Selim Cinek7b836392015-12-04 20:02:59 -0800162 mDividers.get(i).layout(0, 0, getWidth(), mDividerHeight);
Selim Cinekb5605e52015-02-20 18:21:41 +0100163 }
Selim Cinekc897bd32016-03-18 17:32:31 -0700164 if (mOverflowNumber != null) {
Selim Cinek09b7dea2016-08-30 11:28:19 -0700165 boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
166 int left = (isRtl ? 0 : getWidth() - mOverflowNumber.getMeasuredWidth());
167 int right = left + mOverflowNumber.getMeasuredWidth();
168 mOverflowNumber.layout(left, 0, right, mOverflowNumber.getMeasuredHeight());
Selim Cinekc897bd32016-03-18 17:32:31 -0700169 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700170 if (mNotificationHeader != null) {
171 mNotificationHeader.layout(0, 0, mNotificationHeader.getMeasuredWidth(),
172 mNotificationHeader.getMeasuredHeight());
173 }
Selim Cinek414ad332017-02-24 19:06:12 -0800174 if (mNotificationHeaderLowPriority != null) {
175 mNotificationHeaderLowPriority.layout(0, 0,
176 mNotificationHeaderLowPriority.getMeasuredWidth(),
177 mNotificationHeaderLowPriority.getMeasuredHeight());
178 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100179 }
180
181 @Override
182 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100183 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
184 boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
185 boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST;
Selim Cineka69f2a62015-12-11 17:28:12 -0800186 int size = MeasureSpec.getSize(heightMeasureSpec);
Geoffrey Pitsch4dd50062016-12-06 16:41:22 -0500187 int newHeightSpec = heightMeasureSpec;
Selim Cinekb5605e52015-02-20 18:21:41 +0100188 if (hasFixedHeight || isHeightLimited) {
Geoffrey Pitsch4dd50062016-12-06 16:41:22 -0500189 newHeightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
Selim Cinekb5605e52015-02-20 18:21:41 +0100190 }
Selim Cinekc897bd32016-03-18 17:32:31 -0700191 int width = MeasureSpec.getSize(widthMeasureSpec);
192 if (mOverflowNumber != null) {
193 mOverflowNumber.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
194 newHeightSpec);
195 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100196 int dividerHeightSpec = MeasureSpec.makeMeasureSpec(mDividerHeight, MeasureSpec.EXACTLY);
Mady Mellorb0a82462016-04-30 17:31:02 -0700197 int height = mNotificationHeaderMargin + mNotificatonTopPadding;
Selim Cinek471e31a2015-12-11 13:39:48 -0800198 int childCount = Math.min(mChildren.size(), NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
Selim Cinekc897bd32016-03-18 17:32:31 -0700199 int collapsedChildren = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
200 int overflowIndex = childCount > collapsedChildren ? collapsedChildren - 1 : -1;
Selim Cinekb5605e52015-02-20 18:21:41 +0100201 for (int i = 0; i < childCount; i++) {
Selim Cinekc897bd32016-03-18 17:32:31 -0700202 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinekfa760d42016-05-10 15:50:53 -0400203 // We need to measure all children even the GONE ones, such that the heights are
204 // calculated correctly as they are used to calculate how many we can fit on the screen.
Selim Cinekc897bd32016-03-18 17:32:31 -0700205 boolean isOverflow = i == overflowIndex;
Selim Cinek7927c732019-03-06 15:21:39 -0800206 child.setSingleLineWidthIndention(isOverflow && mOverflowNumber != null
Lucas Dupin4c797d62018-05-07 15:32:13 -0700207 ? mOverflowNumber.getMeasuredWidth() : 0);
Selim Cinekb5605e52015-02-20 18:21:41 +0100208 child.measure(widthMeasureSpec, newHeightSpec);
Selim Cinek7b836392015-12-04 20:02:59 -0800209 // layout the divider
210 View divider = mDividers.get(i);
211 divider.measure(widthMeasureSpec, dividerHeightSpec);
Selim Cinekfa760d42016-05-10 15:50:53 -0400212 if (child.getVisibility() != GONE) {
213 height += child.getMeasuredHeight() + mDividerHeight;
214 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100215 }
Selim Cineka69f2a62015-12-11 17:28:12 -0800216 mRealHeight = height;
217 if (heightMode != MeasureSpec.UNSPECIFIED) {
218 height = Math.min(height, size);
219 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700220
Selim Cinek414ad332017-02-24 19:06:12 -0800221 int headerHeightSpec = MeasureSpec.makeMeasureSpec(mHeaderHeight, MeasureSpec.EXACTLY);
Mady Mellorb0a82462016-04-30 17:31:02 -0700222 if (mNotificationHeader != null) {
Mady Mellorb0a82462016-04-30 17:31:02 -0700223 mNotificationHeader.measure(widthMeasureSpec, headerHeightSpec);
224 }
Selim Cinek414ad332017-02-24 19:06:12 -0800225 if (mNotificationHeaderLowPriority != null) {
226 headerHeightSpec = MeasureSpec.makeMeasureSpec(mHeaderHeight, MeasureSpec.EXACTLY);
227 mNotificationHeaderLowPriority.measure(widthMeasureSpec, headerHeightSpec);
228 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700229
Selim Cinekb5605e52015-02-20 18:21:41 +0100230 setMeasuredDimension(width, height);
231 }
232
Selim Cineka69f2a62015-12-11 17:28:12 -0800233 @Override
Selim Cinek0242fbb2016-10-19 13:38:32 -0700234 public boolean hasOverlappingRendering() {
235 return false;
236 }
237
238 @Override
Selim Cineka69f2a62015-12-11 17:28:12 -0800239 public boolean pointInView(float localX, float localY, float slop) {
240 return localX >= -slop && localY >= -slop && localX < ((mRight - mLeft) + slop) &&
241 localY < (mRealHeight + slop);
242 }
243
Selim Cinekb5605e52015-02-20 18:21:41 +0100244 /**
245 * Add a child notification to this view.
246 *
247 * @param row the row to add
248 * @param childIndex the index to add it at, if -1 it will be added at the end
249 */
250 public void addNotification(ExpandableNotificationRow row, int childIndex) {
251 int newIndex = childIndex < 0 ? mChildren.size() : childIndex;
252 mChildren.add(newIndex, row);
253 addView(row);
Selim Cinek8e0c4982016-02-29 20:03:08 -0800254 row.setUserLocked(mUserLocked);
Selim Cinek7b836392015-12-04 20:02:59 -0800255
256 View divider = inflateDivider();
257 addView(divider);
258 mDividers.add(newIndex, divider);
Selim Cinekc897bd32016-03-18 17:32:31 -0700259
260 updateGroupOverflow();
Selim Cinek2b549f42016-11-22 16:38:51 -0800261 row.setContentTransformationAmount(0, false /* isLastChild */);
Selim Cinekf93bf3e2018-05-08 14:43:21 -0700262 // It doesn't make sense to keep old animations around, lets cancel them!
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500263 ExpandableViewState viewState = row.getViewState();
Selim Cinekf93bf3e2018-05-08 14:43:21 -0700264 if (viewState != null) {
265 viewState.cancelAnimations(row);
266 row.cancelAppearDrawing();
267 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100268 }
269
270 public void removeNotification(ExpandableNotificationRow row) {
271 int childIndex = mChildren.indexOf(row);
272 mChildren.remove(row);
273 removeView(row);
Selim Cinek7b836392015-12-04 20:02:59 -0800274
Selim Cinek7b73a4c2016-01-12 18:32:11 -0800275 final View divider = mDividers.remove(childIndex);
Selim Cinek7b836392015-12-04 20:02:59 -0800276 removeView(divider);
Selim Cinek7b73a4c2016-01-12 18:32:11 -0800277 getOverlay().add(divider);
278 CrossFadeHelper.fadeOut(divider, new Runnable() {
279 @Override
280 public void run() {
281 getOverlay().remove(divider);
282 }
283 });
Selim Cinek7b836392015-12-04 20:02:59 -0800284
Selim Cinekb5605e52015-02-20 18:21:41 +0100285 row.setSystemChildExpanded(false);
Selim Cinek8e0c4982016-02-29 20:03:08 -0800286 row.setUserLocked(false);
Selim Cinekc897bd32016-03-18 17:32:31 -0700287 updateGroupOverflow();
Mady Mellorb0a82462016-04-30 17:31:02 -0700288 if (!row.isRemoved()) {
289 mHeaderUtil.restoreNotificationHeader(row);
290 }
291 }
292
293 /**
294 * @return The number of notification children in the container.
295 */
296 public int getNotificationChildCount() {
297 return mChildren.size();
298 }
299
Selim Cinek414ad332017-02-24 19:06:12 -0800300 public void recreateNotificationHeader(OnClickListener listener) {
301 mHeaderClickListener = listener;
302 StatusBarNotification notification = mContainingNotification.getStatusBarNotification();
Mady Mellorb0a82462016-04-30 17:31:02 -0700303 final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(),
Selim Cinek414ad332017-02-24 19:06:12 -0800304 notification.getNotification());
Lucas Dupin00be88f2019-01-03 17:50:52 -0800305 RemoteViews header = builder.makeNotificationHeader();
Mady Mellorb0a82462016-04-30 17:31:02 -0700306 if (mNotificationHeader == null) {
307 mNotificationHeader = (NotificationHeaderView) header.apply(getContext(), this);
308 final View expandButton = mNotificationHeader.findViewById(
309 com.android.internal.R.id.expand_button);
310 expandButton.setVisibility(VISIBLE);
Selim Cinek414ad332017-02-24 19:06:12 -0800311 mNotificationHeader.setOnClickListener(mHeaderClickListener);
Mady Mellorb0a82462016-04-30 17:31:02 -0700312 mNotificationHeaderWrapper = NotificationViewWrapper.wrap(getContext(),
Selim Cinek414ad332017-02-24 19:06:12 -0800313 mNotificationHeader, mContainingNotification);
Mady Mellorb0a82462016-04-30 17:31:02 -0700314 addView(mNotificationHeader, 0);
315 invalidate();
316 } else {
317 header.reapply(getContext(), mNotificationHeader);
Mady Mellorb0a82462016-04-30 17:31:02 -0700318 }
Selim Cinek131f1a42017-06-05 17:50:19 -0700319 mNotificationHeaderWrapper.onContentUpdated(mContainingNotification);
Selim Cinek414ad332017-02-24 19:06:12 -0800320 recreateLowPriorityHeader(builder);
Lucas Dupin6ea27872017-05-30 12:00:04 -0700321 updateHeaderVisibility(false /* animate */);
Mady Mellorb0a82462016-04-30 17:31:02 -0700322 updateChildrenHeaderAppearance();
323 }
324
Selim Cinek414ad332017-02-24 19:06:12 -0800325 /**
326 * Recreate the low-priority header.
327 *
328 * @param builder a builder to reuse. Otherwise the builder will be recovered.
329 */
330 private void recreateLowPriorityHeader(Notification.Builder builder) {
331 RemoteViews header;
332 StatusBarNotification notification = mContainingNotification.getStatusBarNotification();
333 if (mIsLowPriority) {
334 if (builder == null) {
335 builder = Notification.Builder.recoverBuilder(getContext(),
336 notification.getNotification());
337 }
338 header = builder.makeLowPriorityContentView(true /* useRegularSubtext */);
339 if (mNotificationHeaderLowPriority == null) {
340 mNotificationHeaderLowPriority = (NotificationHeaderView) header.apply(getContext(),
341 this);
342 final View expandButton = mNotificationHeaderLowPriority.findViewById(
343 com.android.internal.R.id.expand_button);
344 expandButton.setVisibility(VISIBLE);
345 mNotificationHeaderLowPriority.setOnClickListener(mHeaderClickListener);
346 mNotificationHeaderWrapperLowPriority = NotificationViewWrapper.wrap(getContext(),
347 mNotificationHeaderLowPriority, mContainingNotification);
348 addView(mNotificationHeaderLowPriority, 0);
349 invalidate();
350 } else {
351 header.reapply(getContext(), mNotificationHeaderLowPriority);
352 }
Selim Cinek131f1a42017-06-05 17:50:19 -0700353 mNotificationHeaderWrapperLowPriority.onContentUpdated(mContainingNotification);
Adrian Roos6f6e1592017-05-02 16:22:53 -0700354 resetHeaderVisibilityIfNeeded(mNotificationHeaderLowPriority, calculateDesiredHeader());
Selim Cinek414ad332017-02-24 19:06:12 -0800355 } else {
Selim Cinek6fd06b52017-03-07 15:54:10 -0800356 removeView(mNotificationHeaderLowPriority);
Selim Cinek414ad332017-02-24 19:06:12 -0800357 mNotificationHeaderLowPriority = null;
358 mNotificationHeaderWrapperLowPriority = null;
359 }
360 }
361
Mady Mellorb0a82462016-04-30 17:31:02 -0700362 public void updateChildrenHeaderAppearance() {
363 mHeaderUtil.updateChildrenHeaderAppearance();
Selim Cinekc897bd32016-03-18 17:32:31 -0700364 }
365
366 public void updateGroupOverflow() {
367 int childCount = mChildren.size();
368 int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
369 if (childCount > maxAllowedVisibleChildren) {
Lucas Dupin4c797d62018-05-07 15:32:13 -0700370 int number = childCount - maxAllowedVisibleChildren;
371 mOverflowNumber = mHybridGroupManager.bindOverflowNumber(mOverflowNumber, number);
Selim Cinekc897bd32016-03-18 17:32:31 -0700372 if (mGroupOverFlowState == null) {
373 mGroupOverFlowState = new ViewState();
374 mNeverAppliedGroupState = true;
375 }
376 } else if (mOverflowNumber != null) {
377 removeView(mOverflowNumber);
Eliot Courtneybb8af1c2018-01-11 16:44:45 +0900378 if (isShown() && isAttachedToWindow()) {
Selim Cinekc897bd32016-03-18 17:32:31 -0700379 final View removedOverflowNumber = mOverflowNumber;
380 addTransientView(removedOverflowNumber, getTransientViewCount());
381 CrossFadeHelper.fadeOut(removedOverflowNumber, new Runnable() {
382 @Override
383 public void run() {
384 removeTransientView(removedOverflowNumber);
385 }
386 });
387 }
388 mOverflowNumber = null;
Selim Cinekc897bd32016-03-18 17:32:31 -0700389 mGroupOverFlowState = null;
390 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100391 }
392
Selim Cineka3d3b912016-02-02 11:22:06 -0800393 @Override
394 protected void onConfigurationChanged(Configuration newConfig) {
395 super.onConfigurationChanged(newConfig);
Selim Cinekc897bd32016-03-18 17:32:31 -0700396 updateGroupOverflow();
Selim Cineka3d3b912016-02-02 11:22:06 -0800397 }
398
Selim Cinekb5605e52015-02-20 18:21:41 +0100399 private View inflateDivider() {
400 return LayoutInflater.from(mContext).inflate(
401 R.layout.notification_children_divider, this, false);
402 }
403
404 public List<ExpandableNotificationRow> getNotificationChildren() {
405 return mChildren;
406 }
407
408 /**
409 * Apply the order given in the list to the children.
410 *
411 * @param childOrder the new list order
Selim Cineka7d4f822016-12-06 14:34:47 -0800412 * @param visualStabilityManager
413 * @param callback
Selim Cinekb5605e52015-02-20 18:21:41 +0100414 * @return whether the list order has changed
415 */
Selim Cineka7d4f822016-12-06 14:34:47 -0800416 public boolean applyChildOrder(List<ExpandableNotificationRow> childOrder,
417 VisualStabilityManager visualStabilityManager,
418 VisualStabilityManager.Callback callback) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100419 if (childOrder == null) {
420 return false;
421 }
422 boolean result = false;
423 for (int i = 0; i < mChildren.size() && i < childOrder.size(); i++) {
424 ExpandableNotificationRow child = mChildren.get(i);
425 ExpandableNotificationRow desiredChild = childOrder.get(i);
426 if (child != desiredChild) {
Selim Cineka7d4f822016-12-06 14:34:47 -0800427 if (visualStabilityManager.canReorderNotification(desiredChild)) {
428 mChildren.remove(desiredChild);
429 mChildren.add(i, desiredChild);
430 result = true;
431 } else {
432 visualStabilityManager.addReorderingAllowedCallback(callback);
433 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100434 }
435 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700436 updateExpansionStates();
Selim Cinekb5605e52015-02-20 18:21:41 +0100437 return result;
438 }
439
Selim Cinek83bc7832015-10-22 13:26:54 -0700440 private void updateExpansionStates() {
Selim Cinek898d1732016-02-29 19:57:35 -0800441 if (mChildrenExpanded || mUserLocked) {
442 // we don't modify it the group is expanded or if we are expanding it
443 return;
444 }
445 int size = mChildren.size();
446 for (int i = 0; i < size; i++) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700447 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinek898d1732016-02-29 19:57:35 -0800448 child.setSystemChildExpanded(i == 0 && size == 1);
Selim Cinek83bc7832015-10-22 13:26:54 -0700449 }
450 }
451
452 /**
453 *
454 * @return the intrinsic size of this children container, i.e the natural fully expanded state
455 */
Selim Cinekb5605e52015-02-20 18:21:41 +0100456 public int getIntrinsicHeight() {
Selim Cinek83bc7832015-10-22 13:26:54 -0700457 int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren();
458 return getIntrinsicHeight(maxAllowedVisibleChildren);
459 }
460
461 /**
462 * @return the intrinsic height with a number of children given
463 * in @param maxAllowedVisibleChildren
464 */
465 private int getIntrinsicHeight(float maxAllowedVisibleChildren) {
Selim Cinek414ad332017-02-24 19:06:12 -0800466 if (showingAsLowPriority()) {
467 return mNotificationHeaderLowPriority.getHeight();
468 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800469 int intrinsicHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation;
Selim Cinekb5605e52015-02-20 18:21:41 +0100470 int visibleChildren = 0;
Selim Cinek83bc7832015-10-22 13:26:54 -0700471 int childCount = mChildren.size();
Selim Cinek42357e02016-02-24 18:48:01 -0800472 boolean firstChild = true;
473 float expandFactor = 0;
474 if (mUserLocked) {
Selim Cinekf07d0622016-03-21 19:52:52 -0700475 expandFactor = getGroupExpandFraction();
Selim Cinek42357e02016-02-24 18:48:01 -0800476 }
Selim Cinek7927c732019-03-06 15:21:39 -0800477 boolean childrenExpanded = mChildrenExpanded;
Selim Cinekb5605e52015-02-20 18:21:41 +0100478 for (int i = 0; i < childCount; i++) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700479 if (visibleChildren >= maxAllowedVisibleChildren) {
480 break;
481 }
Selim Cinek42357e02016-02-24 18:48:01 -0800482 if (!firstChild) {
483 if (mUserLocked) {
484 intrinsicHeight += NotificationUtils.interpolate(mChildPadding, mDividerHeight,
485 expandFactor);
486 } else {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700487 intrinsicHeight += childrenExpanded ? mDividerHeight : mChildPadding;
Selim Cinek42357e02016-02-24 18:48:01 -0800488 }
489 } else {
490 if (mUserLocked) {
491 intrinsicHeight += NotificationUtils.interpolate(
492 0,
493 mNotificatonTopPadding + mDividerHeight,
494 expandFactor);
495 } else {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700496 intrinsicHeight += childrenExpanded
Selim Cinek42357e02016-02-24 18:48:01 -0800497 ? mNotificatonTopPadding + mDividerHeight
498 : 0;
499 }
500 firstChild = false;
501 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100502 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinekb5605e52015-02-20 18:21:41 +0100503 intrinsicHeight += child.getIntrinsicHeight();
504 visibleChildren++;
505 }
Selim Cinek42357e02016-02-24 18:48:01 -0800506 if (mUserLocked) {
507 intrinsicHeight += NotificationUtils.interpolate(mCollapsedBottompadding, 0.0f,
508 expandFactor);
Adrian Roos6f6e1592017-05-02 16:22:53 -0700509 } else if (!childrenExpanded) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700510 intrinsicHeight += mCollapsedBottompadding;
511 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100512 return intrinsicHeight;
513 }
514
515 /**
516 * Update the state of all its children based on a linear layout algorithm.
Selim Cinekb5605e52015-02-20 18:21:41 +0100517 * @param parentState the state of the parent
Selim Cinek7927c732019-03-06 15:21:39 -0800518 * @param ambientState the ambient state containing ambient information
Selim Cinekb5605e52015-02-20 18:21:41 +0100519 */
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500520 public void updateState(ExpandableViewState parentState, AmbientState ambientState) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100521 int childCount = mChildren.size();
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800522 int yPosition = mNotificationHeaderMargin + mCurrentHeaderTranslation;
Selim Cinekb5605e52015-02-20 18:21:41 +0100523 boolean firstChild = true;
Selim Cinek83bc7832015-10-22 13:26:54 -0700524 int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren();
Selim Cinek91c2a152016-03-17 15:01:38 -0700525 int lastVisibleIndex = maxAllowedVisibleChildren - 1;
Selim Cinekf07d0622016-03-21 19:52:52 -0700526 int firstOverflowIndex = lastVisibleIndex + 1;
Selim Cinek42357e02016-02-24 18:48:01 -0800527 float expandFactor = 0;
Selim Cinek414ad332017-02-24 19:06:12 -0800528 boolean expandingToExpandedGroup = mUserLocked && !showingAsLowPriority();
Selim Cinek42357e02016-02-24 18:48:01 -0800529 if (mUserLocked) {
Selim Cinekf07d0622016-03-21 19:52:52 -0700530 expandFactor = getGroupExpandFraction();
531 firstOverflowIndex = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
Selim Cinek42357e02016-02-24 18:48:01 -0800532 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700533
Adrian Roos16fe4952017-05-18 16:10:21 -0700534 boolean childrenExpandedAndNotAnimating = mChildrenExpanded
535 && !mContainingNotification.isGroupExpansionChanging();
Selim Cinekc25989e2018-02-16 16:42:14 -0800536 int launchTransitionCompensation = 0;
Selim Cinekb5605e52015-02-20 18:21:41 +0100537 for (int i = 0; i < childCount; i++) {
538 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinekb5605e52015-02-20 18:21:41 +0100539 if (!firstChild) {
Selim Cinek414ad332017-02-24 19:06:12 -0800540 if (expandingToExpandedGroup) {
Mady Mellorb0a82462016-04-30 17:31:02 -0700541 yPosition += NotificationUtils.interpolate(mChildPadding, mDividerHeight,
542 expandFactor);
Selim Cinek42357e02016-02-24 18:48:01 -0800543 } else {
Adrian Roos16fe4952017-05-18 16:10:21 -0700544 yPosition += mChildrenExpanded ? mDividerHeight : mChildPadding;
Selim Cinek42357e02016-02-24 18:48:01 -0800545 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100546 } else {
Selim Cinek414ad332017-02-24 19:06:12 -0800547 if (expandingToExpandedGroup) {
Selim Cinek42357e02016-02-24 18:48:01 -0800548 yPosition += NotificationUtils.interpolate(
549 0,
550 mNotificatonTopPadding + mDividerHeight,
551 expandFactor);
552 } else {
Adrian Roos16fe4952017-05-18 16:10:21 -0700553 yPosition += mChildrenExpanded ? mNotificatonTopPadding + mDividerHeight : 0;
Selim Cinek42357e02016-02-24 18:48:01 -0800554 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100555 firstChild = false;
556 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700557
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500558 ExpandableViewState childState = child.getViewState();
Selim Cinekb5605e52015-02-20 18:21:41 +0100559 int intrinsicHeight = child.getIntrinsicHeight();
Selim Cinekb3dadcc2016-11-21 17:21:13 -0800560 childState.height = intrinsicHeight;
Selim Cinekc25989e2018-02-16 16:42:14 -0800561 childState.yTranslation = yPosition + launchTransitionCompensation;
Selim Cinekb3dadcc2016-11-21 17:21:13 -0800562 childState.hidden = false;
Mady Mellorb0a82462016-04-30 17:31:02 -0700563 // When the group is expanded, the children cast the shadows rather than the parent
564 // so use the parent's elevation here.
Adrian Roos16fe4952017-05-18 16:10:21 -0700565 childState.zTranslation =
566 (childrenExpandedAndNotAnimating && mEnableShadowOnChildNotifications)
Selim Cinekc25989e2018-02-16 16:42:14 -0800567 ? parentState.zTranslation
Mady Mellorb0a82462016-04-30 17:31:02 -0700568 : 0;
Selim Cinekb5605e52015-02-20 18:21:41 +0100569 childState.dimmed = parentState.dimmed;
570 childState.dark = parentState.dark;
571 childState.hideSensitive = parentState.hideSensitive;
Selim Cinekdb167372016-11-17 15:41:17 -0800572 childState.belowSpeedBump = parentState.belowSpeedBump;
Selim Cinekb5605e52015-02-20 18:21:41 +0100573 childState.clipTopAmount = 0;
Selim Cinekf07d0622016-03-21 19:52:52 -0700574 childState.alpha = 0;
575 if (i < firstOverflowIndex) {
Selim Cinek414ad332017-02-24 19:06:12 -0800576 childState.alpha = showingAsLowPriority() ? expandFactor : 1.0f;
Selim Cinekf07d0622016-03-21 19:52:52 -0700577 } else if (expandFactor == 1.0f && i <= lastVisibleIndex) {
578 childState.alpha = (mActualHeight - childState.yTranslation) / childState.height;
579 childState.alpha = Math.max(0.0f, Math.min(1.0f, childState.alpha));
580 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100581 childState.location = parentState.location;
Selim Cineka7d4f822016-12-06 14:34:47 -0800582 childState.inShelf = parentState.inShelf;
Selim Cinekb5605e52015-02-20 18:21:41 +0100583 yPosition += intrinsicHeight;
Selim Cinekc25989e2018-02-16 16:42:14 -0800584 if (child.isExpandAnimationRunning()) {
585 launchTransitionCompensation = -ambientState.getExpandAnimationTopChange();
586 }
Selim Cineka7d4f822016-12-06 14:34:47 -0800587
Selim Cinekb5605e52015-02-20 18:21:41 +0100588 }
Selim Cinekc897bd32016-03-18 17:32:31 -0700589 if (mOverflowNumber != null) {
590 ExpandableNotificationRow overflowView = mChildren.get(Math.min(
Lucas Dupin4c797d62018-05-07 15:32:13 -0700591 getMaxAllowedVisibleChildren(true /* likeCollapsed */), childCount) - 1);
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500592 mGroupOverFlowState.copyFrom(overflowView.getViewState());
Adrian Roos6f6e1592017-05-02 16:22:53 -0700593
Selim Cinek7927c732019-03-06 15:21:39 -0800594 if (!mChildrenExpanded) {
Lucas Dupin4c797d62018-05-07 15:32:13 -0700595 HybridNotificationView alignView = overflowView.getSingleLineView();
Adrian Roos6f6e1592017-05-02 16:22:53 -0700596 if (alignView != null) {
597 View mirrorView = alignView.getTextView();
Selim Cinekc897bd32016-03-18 17:32:31 -0700598 if (mirrorView.getVisibility() == GONE) {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700599 mirrorView = alignView.getTitleView();
Selim Cinekc897bd32016-03-18 17:32:31 -0700600 }
601 if (mirrorView.getVisibility() == GONE) {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700602 mirrorView = alignView;
Selim Cinekc897bd32016-03-18 17:32:31 -0700603 }
Lucas Dupin4c797d62018-05-07 15:32:13 -0700604 mGroupOverFlowState.alpha = mirrorView.getAlpha();
Selim Cinekc897bd32016-03-18 17:32:31 -0700605 mGroupOverFlowState.yTranslation += NotificationUtils.getRelativeYOffset(
606 mirrorView, overflowView);
Selim Cinekc897bd32016-03-18 17:32:31 -0700607 }
608 } else {
Mady Mellorb0a82462016-04-30 17:31:02 -0700609 mGroupOverFlowState.yTranslation += mNotificationHeaderMargin;
Selim Cinekc897bd32016-03-18 17:32:31 -0700610 mGroupOverFlowState.alpha = 0.0f;
611 }
612 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700613 if (mNotificationHeader != null) {
614 if (mHeaderViewState == null) {
615 mHeaderViewState = new ViewState();
616 }
617 mHeaderViewState.initFrom(mNotificationHeader);
Adrian Roos16fe4952017-05-18 16:10:21 -0700618 mHeaderViewState.zTranslation = childrenExpandedAndNotAnimating
Selim Cinekc25989e2018-02-16 16:42:14 -0800619 ? parentState.zTranslation
Mady Mellorb0a82462016-04-30 17:31:02 -0700620 : 0;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800621 mHeaderViewState.yTranslation = mCurrentHeaderTranslation;
622 mHeaderViewState.alpha = mHeaderVisibleAmount;
623 // The hiding is done automatically by the alpha, otherwise we'll pick it up again
624 // in the next frame with the initFrom call above and have an invisible header
625 mHeaderViewState.hidden = false;
Mady Mellorb0a82462016-04-30 17:31:02 -0700626 }
627 }
628
629 /**
630 * When moving into the bottom stack, the bottom visible child in an expanded group adjusts its
631 * height, children in the group after this are gone.
632 *
633 * @param child the child who's height to adjust.
634 * @param parentHeight the height of the parent.
635 * @param childState the state to update.
636 * @param yPosition the yPosition of the view.
637 * @return true if children after this one should be hidden.
638 */
639 private boolean updateChildStateForExpandedGroup(ExpandableNotificationRow child,
Selim Cinekbbcebde2016-11-09 18:28:20 -0800640 int parentHeight, ExpandableViewState childState, int yPosition) {
Mady Mellorb0a82462016-04-30 17:31:02 -0700641 final int top = yPosition + child.getClipTopAmount();
642 final int intrinsicHeight = child.getIntrinsicHeight();
643 final int bottom = top + intrinsicHeight;
644 int newHeight = intrinsicHeight;
645 if (bottom >= parentHeight) {
646 // Child is either clipped or gone
647 newHeight = Math.max((parentHeight - top), 0);
648 }
649 childState.hidden = newHeight == 0;
650 childState.height = newHeight;
651 return childState.height != intrinsicHeight && !childState.hidden;
Selim Cinek83bc7832015-10-22 13:26:54 -0700652 }
653
Aaron Heuckroth17ce40e2018-06-19 15:44:40 -0400654 @VisibleForTesting
655 int getMaxAllowedVisibleChildren() {
Selim Cinek83bc7832015-10-22 13:26:54 -0700656 return getMaxAllowedVisibleChildren(false /* likeCollapsed */);
657 }
658
Aaron Heuckroth17ce40e2018-06-19 15:44:40 -0400659 @VisibleForTesting
660 int getMaxAllowedVisibleChildren(boolean likeCollapsed) {
Aaron Heuckroth17ce40e2018-06-19 15:44:40 -0400661 if (!likeCollapsed && (mChildrenExpanded || mContainingNotification.isUserLocked())
662 && !showingAsLowPriority()) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700663 return NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED;
664 }
Selim Cinek414ad332017-02-24 19:06:12 -0800665 if (mIsLowPriority || !mContainingNotification.isOnKeyguard()
666 && (mContainingNotification.isExpanded() || mContainingNotification.isHeadsUp())) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700667 return NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED;
668 }
669 return NUMBER_OF_CHILDREN_WHEN_COLLAPSED;
Selim Cinekb5605e52015-02-20 18:21:41 +0100670 }
671
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500672 /** Applies state to children. */
673 public void applyState() {
Selim Cinekb5605e52015-02-20 18:21:41 +0100674 int childCount = mChildren.size();
Selim Cinek83bc7832015-10-22 13:26:54 -0700675 ViewState tmpState = new ViewState();
Selim Cinekc897bd32016-03-18 17:32:31 -0700676 float expandFraction = 0.0f;
677 if (mUserLocked) {
Selim Cinekf07d0622016-03-21 19:52:52 -0700678 expandFraction = getGroupExpandFraction();
Selim Cinekc897bd32016-03-18 17:32:31 -0700679 }
Selim Cinek414ad332017-02-24 19:06:12 -0800680 final boolean dividersVisible = mUserLocked && !showingAsLowPriority()
Anthony Chen6bf88a02017-04-10 14:41:44 -0700681 || (mChildrenExpanded && mShowDividersWhenExpanded)
682 || (mContainingNotification.isGroupExpansionChanging()
683 && !mHideDividersDuringExpand);
Selim Cinekb5605e52015-02-20 18:21:41 +0100684 for (int i = 0; i < childCount; i++) {
685 ExpandableNotificationRow child = mChildren.get(i);
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500686 ExpandableViewState viewState = child.getViewState();
Selim Cinekbbcebde2016-11-09 18:28:20 -0800687 viewState.applyToView(child);
Selim Cinek7b836392015-12-04 20:02:59 -0800688
689 // layout the divider
690 View divider = mDividers.get(i);
691 tmpState.initFrom(divider);
692 tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
Anthony Chen6bf88a02017-04-10 14:41:44 -0700693 float alpha = mChildrenExpanded && viewState.alpha != 0 ? mDividerAlpha : 0;
Selim Cinek414ad332017-02-24 19:06:12 -0800694 if (mUserLocked && !showingAsLowPriority() && viewState.alpha != 0) {
Selim Cinekf07d0622016-03-21 19:52:52 -0700695 alpha = NotificationUtils.interpolate(0, 0.5f,
696 Math.min(viewState.alpha, expandFraction));
Selim Cinek42357e02016-02-24 18:48:01 -0800697 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700698 tmpState.hidden = !dividersVisible;
Selim Cinek42357e02016-02-24 18:48:01 -0800699 tmpState.alpha = alpha;
Selim Cinekbbcebde2016-11-09 18:28:20 -0800700 tmpState.applyToView(divider);
Selim Cinekd9a3f8f2016-03-17 19:39:00 -0700701 // There is no fake shadow to be drawn on the children
702 child.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
Selim Cinekb5605e52015-02-20 18:21:41 +0100703 }
Selim Cinek0cfbef42016-11-09 19:06:36 -0800704 if (mGroupOverFlowState != null) {
Selim Cinekbbcebde2016-11-09 18:28:20 -0800705 mGroupOverFlowState.applyToView(mOverflowNumber);
Selim Cinekc897bd32016-03-18 17:32:31 -0700706 mNeverAppliedGroupState = false;
707 }
Selim Cinek0cfbef42016-11-09 19:06:36 -0800708 if (mHeaderViewState != null) {
Selim Cinekbbcebde2016-11-09 18:28:20 -0800709 mHeaderViewState.applyToView(mNotificationHeader);
Mady Mellorb0a82462016-04-30 17:31:02 -0700710 }
Selim Cinekb3dadcc2016-11-21 17:21:13 -0800711 updateChildrenClipping();
712 }
713
714 private void updateChildrenClipping() {
Selim Cinekc25989e2018-02-16 16:42:14 -0800715 if (mContainingNotification.hasExpandingChild()) {
716 return;
717 }
Selim Cinekb3dadcc2016-11-21 17:21:13 -0800718 int childCount = mChildren.size();
Selim Cinek414ad332017-02-24 19:06:12 -0800719 int layoutEnd = mContainingNotification.getActualHeight() - mClipBottomAmount;
Selim Cinekb3dadcc2016-11-21 17:21:13 -0800720 for (int i = 0; i < childCount; i++) {
721 ExpandableNotificationRow child = mChildren.get(i);
722 if (child.getVisibility() == GONE) {
723 continue;
724 }
725 float childTop = child.getTranslationY();
726 float childBottom = childTop + child.getActualHeight();
727 boolean visible = true;
728 int clipBottomAmount = 0;
729 if (childTop > layoutEnd) {
730 visible = false;
731 } else if (childBottom > layoutEnd) {
732 clipBottomAmount = (int) (childBottom - layoutEnd);
733 }
734
735 boolean isVisible = child.getVisibility() == VISIBLE;
736 if (visible != isVisible) {
737 child.setVisibility(visible ? VISIBLE : INVISIBLE);
738 }
739
740 child.setClipBottomAmount(clipBottomAmount);
741 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100742 }
743
Selim Cinekb5605e52015-02-20 18:21:41 +0100744 /**
745 * This is called when the children expansion has changed and positions the children properly
746 * for an appear animation.
747 *
Selim Cinekb5605e52015-02-20 18:21:41 +0100748 */
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500749 public void prepareExpansionChanged() {
Selim Cinek277a8aa2016-01-22 12:12:37 -0800750 // TODO: do something that makes sense, like placing the invisible views correctly
751 return;
Selim Cinekb5605e52015-02-20 18:21:41 +0100752 }
753
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500754 /** Animate to a given state. */
755 public void startAnimationToState(AnimationProperties properties) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100756 int childCount = mChildren.size();
Selim Cinek83bc7832015-10-22 13:26:54 -0700757 ViewState tmpState = new ViewState();
Selim Cinekf07d0622016-03-21 19:52:52 -0700758 float expandFraction = getGroupExpandFraction();
Selim Cinek414ad332017-02-24 19:06:12 -0800759 final boolean dividersVisible = mUserLocked && !showingAsLowPriority()
Anthony Chen6bf88a02017-04-10 14:41:44 -0700760 || (mChildrenExpanded && mShowDividersWhenExpanded)
761 || (mContainingNotification.isGroupExpansionChanging()
762 && !mHideDividersDuringExpand);
Selim Cineke8126522015-12-08 21:21:17 -0800763 for (int i = childCount - 1; i >= 0; i--) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100764 ExpandableNotificationRow child = mChildren.get(i);
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500765 ExpandableViewState viewState = child.getViewState();
Selim Cinek0cfbef42016-11-09 19:06:36 -0800766 viewState.animateTo(child, properties);
Selim Cinek7b836392015-12-04 20:02:59 -0800767
768 // layout the divider
769 View divider = mDividers.get(i);
770 tmpState.initFrom(divider);
771 tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
Selim Cinek42357e02016-02-24 18:48:01 -0800772 float alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
Selim Cinek414ad332017-02-24 19:06:12 -0800773 if (mUserLocked && !showingAsLowPriority() && viewState.alpha != 0) {
Selim Cinekf07d0622016-03-21 19:52:52 -0700774 alpha = NotificationUtils.interpolate(0, 0.5f,
775 Math.min(viewState.alpha, expandFraction));
Selim Cinek42357e02016-02-24 18:48:01 -0800776 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700777 tmpState.hidden = !dividersVisible;
Selim Cinek42357e02016-02-24 18:48:01 -0800778 tmpState.alpha = alpha;
Selim Cinek0cfbef42016-11-09 19:06:36 -0800779 tmpState.animateTo(divider, properties);
Selim Cinekd9a3f8f2016-03-17 19:39:00 -0700780 // There is no fake shadow to be drawn on the children
781 child.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
Selim Cinekb5605e52015-02-20 18:21:41 +0100782 }
Selim Cinekc897bd32016-03-18 17:32:31 -0700783 if (mOverflowNumber != null) {
784 if (mNeverAppliedGroupState) {
785 float alpha = mGroupOverFlowState.alpha;
786 mGroupOverFlowState.alpha = 0;
Selim Cinekbbcebde2016-11-09 18:28:20 -0800787 mGroupOverFlowState.applyToView(mOverflowNumber);
Selim Cinekc897bd32016-03-18 17:32:31 -0700788 mGroupOverFlowState.alpha = alpha;
789 mNeverAppliedGroupState = false;
790 }
Selim Cinek0cfbef42016-11-09 19:06:36 -0800791 mGroupOverFlowState.animateTo(mOverflowNumber, properties);
Selim Cinekc897bd32016-03-18 17:32:31 -0700792 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700793 if (mNotificationHeader != null) {
Selim Cinekbbcebde2016-11-09 18:28:20 -0800794 mHeaderViewState.applyToView(mNotificationHeader);
Mady Mellorb0a82462016-04-30 17:31:02 -0700795 }
Selim Cinekb3dadcc2016-11-21 17:21:13 -0800796 updateChildrenClipping();
Selim Cinekb5605e52015-02-20 18:21:41 +0100797 }
798
799 public ExpandableNotificationRow getViewAtPosition(float y) {
800 // find the view under the pointer, accounting for GONE views
801 final int count = mChildren.size();
802 for (int childIdx = 0; childIdx < count; childIdx++) {
803 ExpandableNotificationRow slidingChild = mChildren.get(childIdx);
804 float childTop = slidingChild.getTranslationY();
805 float top = childTop + slidingChild.getClipTopAmount();
806 float bottom = childTop + slidingChild.getActualHeight();
807 if (y >= top && y <= bottom) {
808 return slidingChild;
809 }
810 }
811 return null;
812 }
813
Selim Cinek83bc7832015-10-22 13:26:54 -0700814 public void setChildrenExpanded(boolean childrenExpanded) {
815 mChildrenExpanded = childrenExpanded;
Selim Cinek898d1732016-02-29 19:57:35 -0800816 updateExpansionStates();
Mady Mellorb0a82462016-04-30 17:31:02 -0700817 if (mNotificationHeader != null) {
818 mNotificationHeader.setExpanded(childrenExpanded);
819 }
Selim Cinekddf1b392016-05-27 16:33:10 -0700820 final int count = mChildren.size();
821 for (int childIdx = 0; childIdx < count; childIdx++) {
822 ExpandableNotificationRow child = mChildren.get(childIdx);
823 child.setChildrenExpanded(childrenExpanded, false);
824 }
Selim Cinekbc342152018-12-05 18:45:42 -0800825 updateHeaderTouchability();
Selim Cinek83bc7832015-10-22 13:26:54 -0700826 }
827
Selim Cinek414ad332017-02-24 19:06:12 -0800828 public void setContainingNotification(ExpandableNotificationRow parent) {
829 mContainingNotification = parent;
830 mHeaderUtil = new NotificationHeaderUtil(mContainingNotification);
Mady Mellorb0a82462016-04-30 17:31:02 -0700831 }
832
Selim Cinek414ad332017-02-24 19:06:12 -0800833 public ExpandableNotificationRow getContainingNotification() {
834 return mContainingNotification;
Adrian Roos4a579672016-05-24 16:54:37 -0700835 }
836
Mady Mellorb0a82462016-04-30 17:31:02 -0700837 public NotificationHeaderView getHeaderView() {
838 return mNotificationHeader;
839 }
840
Selim Cinek414ad332017-02-24 19:06:12 -0800841 public NotificationHeaderView getLowPriorityHeaderView() {
842 return mNotificationHeaderLowPriority;
843 }
844
Lucas Dupin6ea27872017-05-30 12:00:04 -0700845 @VisibleForTesting
846 public ViewGroup getCurrentHeaderView() {
847 return mCurrentHeader;
848 }
849
Selim Cinek414ad332017-02-24 19:06:12 -0800850 private void updateHeaderVisibility(boolean animate) {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700851 ViewGroup desiredHeader;
852 ViewGroup currentHeader = mCurrentHeader;
853 desiredHeader = calculateDesiredHeader();
854
855 if (currentHeader == desiredHeader) {
856 return;
Mady Mellorb0a82462016-04-30 17:31:02 -0700857 }
Adrian Roos6f6e1592017-05-02 16:22:53 -0700858
Selim Cinek414ad332017-02-24 19:06:12 -0800859 if (animate) {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700860 if (desiredHeader != null && currentHeader != null) {
861 currentHeader.setVisibility(VISIBLE);
862 desiredHeader.setVisibility(VISIBLE);
863 NotificationViewWrapper visibleWrapper = getWrapperForView(desiredHeader);
864 NotificationViewWrapper hiddenWrapper = getWrapperForView(currentHeader);
Selim Cinek414ad332017-02-24 19:06:12 -0800865 visibleWrapper.transformFrom(hiddenWrapper);
866 hiddenWrapper.transformTo(visibleWrapper, () -> updateHeaderVisibility(false));
Adrian Roos6f6e1592017-05-02 16:22:53 -0700867 startChildAlphaAnimations(desiredHeader == mNotificationHeader);
Selim Cinek414ad332017-02-24 19:06:12 -0800868 } else {
869 animate = false;
870 }
871 }
872 if (!animate) {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700873 if (desiredHeader != null) {
874 getWrapperForView(desiredHeader).setVisible(true);
875 desiredHeader.setVisibility(VISIBLE);
Selim Cinek414ad332017-02-24 19:06:12 -0800876 }
Adrian Roos6f6e1592017-05-02 16:22:53 -0700877 if (currentHeader != null) {
Lucas Dupin6ea27872017-05-30 12:00:04 -0700878 // Wrapper can be null if we were a low priority notification
879 // and just destroyed it by calling setIsLowPriority(false)
880 NotificationViewWrapper wrapper = getWrapperForView(currentHeader);
881 if (wrapper != null) {
882 wrapper.setVisible(false);
883 }
Adrian Roos6f6e1592017-05-02 16:22:53 -0700884 currentHeader.setVisibility(INVISIBLE);
Selim Cinek414ad332017-02-24 19:06:12 -0800885 }
886 }
Adrian Roos6f6e1592017-05-02 16:22:53 -0700887
888 resetHeaderVisibilityIfNeeded(mNotificationHeader, desiredHeader);
Adrian Roos6f6e1592017-05-02 16:22:53 -0700889 resetHeaderVisibilityIfNeeded(mNotificationHeaderLowPriority, desiredHeader);
890
Lucas Dupin6ea27872017-05-30 12:00:04 -0700891 mCurrentHeader = desiredHeader;
Adrian Roos6f6e1592017-05-02 16:22:53 -0700892 }
893
894 private void resetHeaderVisibilityIfNeeded(View header, View desiredHeader) {
895 if (header == null) {
896 return;
897 }
898 if (header != mCurrentHeader && header != desiredHeader) {
899 getWrapperForView(header).setVisible(false);
900 header.setVisibility(INVISIBLE);
901 }
902 if (header == desiredHeader && header.getVisibility() != VISIBLE) {
903 getWrapperForView(header).setVisible(true);
904 header.setVisibility(VISIBLE);
905 }
906 }
907
908 private ViewGroup calculateDesiredHeader() {
909 ViewGroup desiredHeader;
Selim Cinek7927c732019-03-06 15:21:39 -0800910 if (showingAsLowPriority()) {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700911 desiredHeader = mNotificationHeaderLowPriority;
912 } else {
913 desiredHeader = mNotificationHeader;
914 }
915 return desiredHeader;
Selim Cinek414ad332017-02-24 19:06:12 -0800916 }
917
918 private void startChildAlphaAnimations(boolean toVisible) {
919 float target = toVisible ? 1.0f : 0.0f;
920 float start = 1.0f - target;
921 int childCount = mChildren.size();
922 for (int i = 0; i < childCount; i++) {
923 if (i >= NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED) {
924 break;
925 }
926 ExpandableNotificationRow child = mChildren.get(i);
927 child.setAlpha(start);
928 ViewState viewState = new ViewState();
929 viewState.initFrom(child);
930 viewState.alpha = target;
931 ALPHA_FADE_IN.setDelay(i * 50);
932 viewState.animateTo(child, ALPHA_FADE_IN);
933 }
934 }
935
936
937 private void updateHeaderTransformation() {
Selim Cinek1826d982017-03-06 20:47:37 -0800938 if (mUserLocked && showingAsLowPriority()) {
Selim Cinek414ad332017-02-24 19:06:12 -0800939 float fraction = getGroupExpandFraction();
940 mNotificationHeaderWrapper.transformFrom(mNotificationHeaderWrapperLowPriority,
941 fraction);
942 mNotificationHeader.setVisibility(VISIBLE);
943 mNotificationHeaderWrapperLowPriority.transformTo(mNotificationHeaderWrapper,
944 fraction);
945 }
946
947 }
948
Adrian Roos6f6e1592017-05-02 16:22:53 -0700949 private NotificationViewWrapper getWrapperForView(View visibleHeader) {
Selim Cinek414ad332017-02-24 19:06:12 -0800950 if (visibleHeader == mNotificationHeader) {
951 return mNotificationHeaderWrapper;
952 }
953 return mNotificationHeaderWrapperLowPriority;
Mady Mellorb0a82462016-04-30 17:31:02 -0700954 }
955
956 /**
957 * Called when a groups expansion changes to adjust the background of the header view.
958 *
959 * @param expanded whether the group is expanded.
960 */
961 public void updateHeaderForExpansion(boolean expanded) {
962 if (mNotificationHeader != null) {
963 if (expanded) {
964 ColorDrawable cd = new ColorDrawable();
Selim Cinek414ad332017-02-24 19:06:12 -0800965 cd.setColor(mContainingNotification.calculateBgColor());
Mady Mellorb0a82462016-04-30 17:31:02 -0700966 mNotificationHeader.setHeaderBackgroundDrawable(cd);
967 } else {
968 mNotificationHeader.setHeaderBackgroundDrawable(null);
969 }
970 }
Selim Cinek388df6d2015-10-22 13:25:11 -0700971 }
972
Selim Cinek83bc7832015-10-22 13:26:54 -0700973 public int getMaxContentHeight() {
Selim Cinek414ad332017-02-24 19:06:12 -0800974 if (showingAsLowPriority()) {
975 return getMinHeight(NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED, true
976 /* likeHighPriority */);
977 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800978 int maxContentHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation
979 + mNotificatonTopPadding;
Selim Cinek42357e02016-02-24 18:48:01 -0800980 int visibleChildren = 0;
981 int childCount = mChildren.size();
982 for (int i = 0; i < childCount; i++) {
983 if (visibleChildren >= NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED) {
984 break;
985 }
986 ExpandableNotificationRow child = mChildren.get(i);
Selim Cineke81b82b2016-03-04 11:22:28 -0800987 float childHeight = child.isExpanded(true /* allowOnKeyguard */)
Selim Cinek42357e02016-02-24 18:48:01 -0800988 ? child.getMaxExpandHeight()
989 : child.getShowingLayout().getMinHeight(true /* likeGroupExpanded */);
990 maxContentHeight += childHeight;
991 visibleChildren++;
992 }
993 if (visibleChildren > 0) {
994 maxContentHeight += visibleChildren * mDividerHeight;
995 }
996 return maxContentHeight;
997 }
998
999 public void setActualHeight(int actualHeight) {
1000 if (!mUserLocked) {
1001 return;
1002 }
1003 mActualHeight = actualHeight;
Selim Cinekf07d0622016-03-21 19:52:52 -07001004 float fraction = getGroupExpandFraction();
Selim Cinek414ad332017-02-24 19:06:12 -08001005 boolean showingLowPriority = showingAsLowPriority();
1006 updateHeaderTransformation();
Selim Cinekf07d0622016-03-21 19:52:52 -07001007 int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* forceCollapsed */);
Selim Cinek42357e02016-02-24 18:48:01 -08001008 int childCount = mChildren.size();
1009 for (int i = 0; i < childCount; i++) {
1010 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinek414ad332017-02-24 19:06:12 -08001011 float childHeight;
1012 if (showingLowPriority) {
1013 childHeight = child.getShowingLayout().getMinHeight(false /* likeGroupExpanded */);
1014 } else if (child.isExpanded(true /* allowOnKeyguard */)) {
1015 childHeight = child.getMaxExpandHeight();
1016 } else {
1017 childHeight = child.getShowingLayout().getMinHeight(
1018 true /* likeGroupExpanded */);
1019 }
Selim Cinekf07d0622016-03-21 19:52:52 -07001020 if (i < maxAllowedVisibleChildren) {
1021 float singleLineHeight = child.getShowingLayout().getMinHeight(
1022 false /* likeGroupExpanded */);
1023 child.setActualHeight((int) NotificationUtils.interpolate(singleLineHeight,
1024 childHeight, fraction), false);
1025 } else {
1026 child.setActualHeight((int) childHeight, false);
1027 }
Selim Cinek42357e02016-02-24 18:48:01 -08001028 }
1029 }
1030
Selim Cinekf07d0622016-03-21 19:52:52 -07001031 public float getGroupExpandFraction() {
Selim Cinek414ad332017-02-24 19:06:12 -08001032 int visibleChildrenExpandedHeight = showingAsLowPriority() ? getMaxContentHeight()
1033 : getVisibleChildrenExpandHeight();
Selim Cinek567e8452016-03-24 10:54:56 -07001034 int minExpandHeight = getCollapsedHeight();
Selim Cinekf07d0622016-03-21 19:52:52 -07001035 float factor = (mActualHeight - minExpandHeight)
1036 / (float) (visibleChildrenExpandedHeight - minExpandHeight);
Selim Cinek42357e02016-02-24 18:48:01 -08001037 return Math.max(0.0f, Math.min(1.0f, factor));
1038 }
1039
Selim Cinekf07d0622016-03-21 19:52:52 -07001040 private int getVisibleChildrenExpandHeight() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001041 int intrinsicHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation
1042 + mNotificatonTopPadding + mDividerHeight;
Selim Cinek42357e02016-02-24 18:48:01 -08001043 int visibleChildren = 0;
1044 int childCount = mChildren.size();
Selim Cinekf07d0622016-03-21 19:52:52 -07001045 int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* forceCollapsed */);
Selim Cinek42357e02016-02-24 18:48:01 -08001046 for (int i = 0; i < childCount; i++) {
Selim Cinekf07d0622016-03-21 19:52:52 -07001047 if (visibleChildren >= maxAllowedVisibleChildren) {
Selim Cinek42357e02016-02-24 18:48:01 -08001048 break;
1049 }
1050 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinekf07d0622016-03-21 19:52:52 -07001051 float childHeight = child.isExpanded(true /* allowOnKeyguard */)
1052 ? child.getMaxExpandHeight()
1053 : child.getShowingLayout().getMinHeight(true /* likeGroupExpanded */);
1054 intrinsicHeight += childHeight;
Selim Cinek42357e02016-02-24 18:48:01 -08001055 visibleChildren++;
1056 }
Selim Cinek42357e02016-02-24 18:48:01 -08001057 return intrinsicHeight;
Selim Cinek83bc7832015-10-22 13:26:54 -07001058 }
1059
1060 public int getMinHeight() {
Selim Cinek7927c732019-03-06 15:21:39 -08001061 return getMinHeight(NUMBER_OF_CHILDREN_WHEN_COLLAPSED, false /* likeHighPriority */);
Selim Cinekb55386d2015-12-16 17:26:49 -08001062 }
1063
Selim Cinek567e8452016-03-24 10:54:56 -07001064 public int getCollapsedHeight() {
Selim Cinek414ad332017-02-24 19:06:12 -08001065 return getMinHeight(getMaxAllowedVisibleChildren(true /* forceCollapsed */),
1066 false /* likeHighPriority */);
Selim Cinek567e8452016-03-24 10:54:56 -07001067 }
1068
Selim Cinek414ad332017-02-24 19:06:12 -08001069 /**
1070 * Get the minimum Height for this group.
1071 *
1072 * @param maxAllowedVisibleChildren the number of children that should be visible
1073 * @param likeHighPriority if the height should be calculated as if it were not low priority
1074 */
1075 private int getMinHeight(int maxAllowedVisibleChildren, boolean likeHighPriority) {
1076 if (!likeHighPriority && showingAsLowPriority()) {
1077 return mNotificationHeaderLowPriority.getHeight();
Selim Cinek6743c0b2017-01-18 18:24:01 -08001078 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001079 int minExpandHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation;
Selim Cinek2c584612016-02-29 16:14:25 -08001080 int visibleChildren = 0;
1081 boolean firstChild = true;
1082 int childCount = mChildren.size();
1083 for (int i = 0; i < childCount; i++) {
1084 if (visibleChildren >= maxAllowedVisibleChildren) {
1085 break;
1086 }
1087 if (!firstChild) {
1088 minExpandHeight += mChildPadding;
1089 } else {
1090 firstChild = false;
1091 }
1092 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinekf1f270a2016-04-05 19:02:37 -07001093 minExpandHeight += child.getSingleLineView().getHeight();
Selim Cinek2c584612016-02-29 16:14:25 -08001094 visibleChildren++;
1095 }
1096 minExpandHeight += mCollapsedBottompadding;
1097 return minExpandHeight;
Selim Cinekb5605e52015-02-20 18:21:41 +01001098 }
Selim Cinek9c7712d2015-12-08 19:19:48 -08001099
Selim Cinek414ad332017-02-24 19:06:12 -08001100 public boolean showingAsLowPriority() {
1101 return mIsLowPriority && !mContainingNotification.isExpanded();
1102 }
1103
Mady Mellorb0a82462016-04-30 17:31:02 -07001104 public void reInflateViews(OnClickListener listener, StatusBarNotification notification) {
Selim Cinek414ad332017-02-24 19:06:12 -08001105 if (mNotificationHeader != null) {
1106 removeView(mNotificationHeader);
1107 mNotificationHeader = null;
1108 }
1109 if (mNotificationHeaderLowPriority != null) {
1110 removeView(mNotificationHeaderLowPriority);
1111 mNotificationHeaderLowPriority = null;
1112 }
1113 recreateNotificationHeader(listener);
Selim Cinek01af3342016-02-09 19:25:31 -08001114 initDimens();
1115 for (int i = 0; i < mDividers.size(); i++) {
1116 View prevDivider = mDividers.get(i);
1117 int index = indexOfChild(prevDivider);
1118 removeView(prevDivider);
1119 View divider = inflateDivider();
1120 addView(divider, index);
1121 mDividers.set(i, divider);
1122 }
Selim Cinek4bb59342016-04-08 19:29:35 -07001123 removeView(mOverflowNumber);
1124 mOverflowNumber = null;
Selim Cinek4bb59342016-04-08 19:29:35 -07001125 mGroupOverFlowState = null;
1126 updateGroupOverflow();
Selim Cinek01af3342016-02-09 19:25:31 -08001127 }
Selim Cinek42357e02016-02-24 18:48:01 -08001128
1129 public void setUserLocked(boolean userLocked) {
1130 mUserLocked = userLocked;
Selim Cinek414ad332017-02-24 19:06:12 -08001131 if (!mUserLocked) {
1132 updateHeaderVisibility(false /* animate */);
1133 }
Selim Cinek42357e02016-02-24 18:48:01 -08001134 int childCount = mChildren.size();
1135 for (int i = 0; i < childCount; i++) {
1136 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinek414ad332017-02-24 19:06:12 -08001137 child.setUserLocked(userLocked && !showingAsLowPriority());
Selim Cinek42357e02016-02-24 18:48:01 -08001138 }
Selim Cinekbc342152018-12-05 18:45:42 -08001139 updateHeaderTouchability();
1140 }
1141
1142 private void updateHeaderTouchability() {
1143 if (mNotificationHeader != null) {
1144 mNotificationHeader.setAcceptAllTouches(mChildrenExpanded || mUserLocked);
1145 }
Selim Cinek42357e02016-02-24 18:48:01 -08001146 }
Selim Cinekc897bd32016-03-18 17:32:31 -07001147
1148 public void onNotificationUpdated() {
1149 mHybridGroupManager.setOverflowNumberColor(mOverflowNumber,
Lucas Dupin00be88f2019-01-03 17:50:52 -08001150 mContainingNotification.getNotificationColor());
Selim Cinekc897bd32016-03-18 17:32:31 -07001151 }
Adrian Roosd009ab12016-05-20 17:58:53 -07001152
Adrian Roos4a579672016-05-24 16:54:37 -07001153 public int getPositionInLinearLayout(View childInGroup) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001154 int position = mNotificationHeaderMargin + mCurrentHeaderTranslation
1155 + mNotificatonTopPadding;
Adrian Roos4a579672016-05-24 16:54:37 -07001156
1157 for (int i = 0; i < mChildren.size(); i++) {
1158 ExpandableNotificationRow child = mChildren.get(i);
1159 boolean notGone = child.getVisibility() != View.GONE;
1160 if (notGone) {
1161 position += mDividerHeight;
1162 }
1163 if (child == childInGroup) {
1164 return position;
1165 }
1166 if (notGone) {
1167 position += child.getIntrinsicHeight();
1168 }
1169 }
1170 return 0;
1171 }
Selim Cinek0242fbb2016-10-19 13:38:32 -07001172
1173 public void setIconsVisible(boolean iconsVisible) {
1174 if (mNotificationHeaderWrapper != null) {
1175 NotificationHeaderView header = mNotificationHeaderWrapper.getNotificationHeader();
1176 if (header != null) {
1177 header.getIcon().setForceHidden(!iconsVisible);
1178 }
1179 }
Selim Cinek414ad332017-02-24 19:06:12 -08001180 if (mNotificationHeaderWrapperLowPriority != null) {
1181 NotificationHeaderView header
1182 = mNotificationHeaderWrapperLowPriority.getNotificationHeader();
1183 if (header != null) {
1184 header.getIcon().setForceHidden(!iconsVisible);
1185 }
1186 }
Selim Cinek0242fbb2016-10-19 13:38:32 -07001187 }
Selim Cinekb3dadcc2016-11-21 17:21:13 -08001188
1189 public void setClipBottomAmount(int clipBottomAmount) {
1190 mClipBottomAmount = clipBottomAmount;
1191 updateChildrenClipping();
1192 }
Selim Cinek6743c0b2017-01-18 18:24:01 -08001193
1194 public void setIsLowPriority(boolean isLowPriority) {
1195 mIsLowPriority = isLowPriority;
Selim Cinek414ad332017-02-24 19:06:12 -08001196 if (mContainingNotification != null) { /* we're not yet set up yet otherwise */
1197 recreateLowPriorityHeader(null /* existingBuilder */);
1198 updateHeaderVisibility(false /* animate */);
1199 }
1200 if (mUserLocked) {
1201 setUserLocked(mUserLocked);
1202 }
1203 }
1204
1205 public NotificationHeaderView getVisibleHeader() {
1206 NotificationHeaderView header = mNotificationHeader;
1207 if (showingAsLowPriority()) {
1208 header = mNotificationHeaderLowPriority;
1209 }
1210 return header;
1211 }
1212
1213 public void onExpansionChanged() {
1214 if (mIsLowPriority) {
1215 if (mUserLocked) {
1216 setUserLocked(mUserLocked);
1217 }
1218 updateHeaderVisibility(true /* animate */);
1219 }
1220 }
1221
1222 public float getIncreasedPaddingAmount() {
1223 if (showingAsLowPriority()) {
1224 return 0.0f;
1225 }
1226 return getGroupExpandFraction();
Selim Cinek6743c0b2017-01-18 18:24:01 -08001227 }
Selim Cinek817abe72017-05-24 11:08:55 -07001228
1229 @VisibleForTesting
1230 public boolean isUserLocked() {
1231 return mUserLocked;
1232 }
Selim Cinek2871bef2017-11-22 08:40:00 -08001233
1234 public void setCurrentBottomRoundness(float currentBottomRoundness) {
1235 boolean last = true;
1236 for (int i = mChildren.size() - 1; i >= 0; i--) {
1237 ExpandableNotificationRow child = mChildren.get(i);
1238 if (child.getVisibility() == View.GONE) {
1239 continue;
1240 }
1241 float bottomRoundness = last ? currentBottomRoundness : 0.0f;
1242 child.setBottomRoundness(bottomRoundness, isShown() /* animate */);
1243 last = false;
1244 }
1245 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001246
1247 public void setHeaderVisibleAmount(float headerVisibleAmount) {
1248 mHeaderVisibleAmount = headerVisibleAmount;
1249 mCurrentHeaderTranslation = (int) ((1.0f - headerVisibleAmount) * mTranslationForHeader);
1250 }
Selim Cinekb5605e52015-02-20 18:21:41 +01001251}