blob: d9e1150035a30a4e8677cdba1a39292c558df642 [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
17package com.android.systemui.statusbar.stack;
18
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;
Selim Cinekb5605e52015-02-20 18:21:41 +010036import com.android.systemui.statusbar.ExpandableNotificationRow;
Mady Mellorb0a82462016-04-30 17:31:02 -070037import com.android.systemui.statusbar.NotificationHeaderUtil;
Selim Cinekc897bd32016-03-18 17:32:31 -070038import com.android.systemui.statusbar.notification.HybridGroupManager;
Selim Cinek83bc7832015-10-22 13:26:54 -070039import com.android.systemui.statusbar.notification.HybridNotificationView;
Selim Cinek42357e02016-02-24 18:48:01 -080040import com.android.systemui.statusbar.notification.NotificationUtils;
Mady Mellorb0a82462016-04-30 17:31:02 -070041import com.android.systemui.statusbar.notification.NotificationViewWrapper;
Selim Cineka7d4f822016-12-06 14:34:47 -080042import com.android.systemui.statusbar.notification.VisualStabilityManager;
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
Selim Cinek83bc7832015-10-22 13:26:54 -070052 private static final int NUMBER_OF_CHILDREN_WHEN_COLLAPSED = 2;
53 private static final int NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED = 5;
54 private static final int NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED = 8;
Lucas Dupin4c797d62018-05-07 15:32:13 -070055 private static final int NUMBER_OF_CHILDREN_WHEN_AMBIENT = 1;
Selim Cinek414ad332017-02-24 19:06:12 -080056 private static final AnimationProperties ALPHA_FADE_IN = new AnimationProperties() {
57 private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha();
58
59 @Override
60 public AnimationFilter getAnimationFilter() {
61 return mAnimationFilter;
62 }
63 }.setDuration(200);
Selim Cinek83bc7832015-10-22 13:26:54 -070064
Selim Cinekb5605e52015-02-20 18:21:41 +010065 private final List<View> mDividers = new ArrayList<>();
66 private final List<ExpandableNotificationRow> mChildren = new ArrayList<>();
Selim Cinekc897bd32016-03-18 17:32:31 -070067 private final HybridGroupManager mHybridGroupManager;
Selim Cinek01af3342016-02-09 19:25:31 -080068 private int mChildPadding;
69 private int mDividerHeight;
Anthony Chen6bf88a02017-04-10 14:41:44 -070070 private float mDividerAlpha;
Mady Mellorb0a82462016-04-30 17:31:02 -070071 private int mNotificationHeaderMargin;
Anthony Chen6bf88a02017-04-10 14:41:44 -070072
Selim Cinek01af3342016-02-09 19:25:31 -080073 private int mNotificatonTopPadding;
74 private float mCollapsedBottompadding;
Selim Cinek83bc7832015-10-22 13:26:54 -070075 private boolean mChildrenExpanded;
Selim Cinek414ad332017-02-24 19:06:12 -080076 private ExpandableNotificationRow mContainingNotification;
Selim Cinekc897bd32016-03-18 17:32:31 -070077 private TextView mOverflowNumber;
78 private ViewState mGroupOverFlowState;
Selim Cineka69f2a62015-12-11 17:28:12 -080079 private int mRealHeight;
Selim Cinek42357e02016-02-24 18:48:01 -080080 private boolean mUserLocked;
81 private int mActualHeight;
Selim Cinekc897bd32016-03-18 17:32:31 -070082 private boolean mNeverAppliedGroupState;
Mady Mellorb0a82462016-04-30 17:31:02 -070083 private int mHeaderHeight;
84
Anthony Chen6bf88a02017-04-10 14:41:44 -070085 /**
86 * Whether or not individual notifications that are part of this container will have shadows.
87 */
88 private boolean mEnableShadowOnChildNotifications;
89
Mady Mellorb0a82462016-04-30 17:31:02 -070090 private NotificationHeaderView mNotificationHeader;
91 private NotificationViewWrapper mNotificationHeaderWrapper;
Selim Cinek414ad332017-02-24 19:06:12 -080092 private NotificationHeaderView mNotificationHeaderLowPriority;
93 private NotificationViewWrapper mNotificationHeaderWrapperLowPriority;
Adrian Roos6f6e1592017-05-02 16:22:53 -070094 private ViewGroup mNotificationHeaderAmbient;
95 private NotificationViewWrapper mNotificationHeaderWrapperAmbient;
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 }
Adrian Roos6f6e1592017-05-02 16:22:53 -0700179 if (mNotificationHeaderAmbient != null) {
180 mNotificationHeaderAmbient.layout(0, 0,
181 mNotificationHeaderAmbient.getMeasuredWidth(),
182 mNotificationHeaderAmbient.getMeasuredHeight());
183 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100184 }
185
186 @Override
187 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100188 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
189 boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
190 boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST;
Selim Cineka69f2a62015-12-11 17:28:12 -0800191 int size = MeasureSpec.getSize(heightMeasureSpec);
Geoffrey Pitsch4dd50062016-12-06 16:41:22 -0500192 int newHeightSpec = heightMeasureSpec;
Selim Cinekb5605e52015-02-20 18:21:41 +0100193 if (hasFixedHeight || isHeightLimited) {
Geoffrey Pitsch4dd50062016-12-06 16:41:22 -0500194 newHeightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
Selim Cinekb5605e52015-02-20 18:21:41 +0100195 }
Selim Cinekc897bd32016-03-18 17:32:31 -0700196 int width = MeasureSpec.getSize(widthMeasureSpec);
197 if (mOverflowNumber != null) {
198 mOverflowNumber.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
199 newHeightSpec);
200 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100201 int dividerHeightSpec = MeasureSpec.makeMeasureSpec(mDividerHeight, MeasureSpec.EXACTLY);
Mady Mellorb0a82462016-04-30 17:31:02 -0700202 int height = mNotificationHeaderMargin + mNotificatonTopPadding;
Selim Cinek471e31a2015-12-11 13:39:48 -0800203 int childCount = Math.min(mChildren.size(), NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
Selim Cinekc897bd32016-03-18 17:32:31 -0700204 int collapsedChildren = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
205 int overflowIndex = childCount > collapsedChildren ? collapsedChildren - 1 : -1;
Selim Cinekb5605e52015-02-20 18:21:41 +0100206 for (int i = 0; i < childCount; i++) {
Selim Cinekc897bd32016-03-18 17:32:31 -0700207 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinekfa760d42016-05-10 15:50:53 -0400208 // We need to measure all children even the GONE ones, such that the heights are
209 // calculated correctly as they are used to calculate how many we can fit on the screen.
Selim Cinekc897bd32016-03-18 17:32:31 -0700210 boolean isOverflow = i == overflowIndex;
Lucas Dupin4c797d62018-05-07 15:32:13 -0700211 child.setSingleLineWidthIndention(isOverflow && mOverflowNumber != null &&
212 !mContainingNotification.isShowingAmbient()
213 ? mOverflowNumber.getMeasuredWidth() : 0);
Selim Cinekb5605e52015-02-20 18:21:41 +0100214 child.measure(widthMeasureSpec, newHeightSpec);
Selim Cinek7b836392015-12-04 20:02:59 -0800215 // layout the divider
216 View divider = mDividers.get(i);
217 divider.measure(widthMeasureSpec, dividerHeightSpec);
Selim Cinekfa760d42016-05-10 15:50:53 -0400218 if (child.getVisibility() != GONE) {
219 height += child.getMeasuredHeight() + mDividerHeight;
220 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100221 }
Selim Cineka69f2a62015-12-11 17:28:12 -0800222 mRealHeight = height;
223 if (heightMode != MeasureSpec.UNSPECIFIED) {
224 height = Math.min(height, size);
225 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700226
Selim Cinek414ad332017-02-24 19:06:12 -0800227 int headerHeightSpec = MeasureSpec.makeMeasureSpec(mHeaderHeight, MeasureSpec.EXACTLY);
Mady Mellorb0a82462016-04-30 17:31:02 -0700228 if (mNotificationHeader != null) {
Mady Mellorb0a82462016-04-30 17:31:02 -0700229 mNotificationHeader.measure(widthMeasureSpec, headerHeightSpec);
230 }
Selim Cinek414ad332017-02-24 19:06:12 -0800231 if (mNotificationHeaderLowPriority != null) {
232 headerHeightSpec = MeasureSpec.makeMeasureSpec(mHeaderHeight, MeasureSpec.EXACTLY);
233 mNotificationHeaderLowPriority.measure(widthMeasureSpec, headerHeightSpec);
234 }
Adrian Roos6f6e1592017-05-02 16:22:53 -0700235 if (mNotificationHeaderAmbient != null) {
236 headerHeightSpec = MeasureSpec.makeMeasureSpec(mHeaderHeight, MeasureSpec.EXACTLY);
237 mNotificationHeaderAmbient.measure(widthMeasureSpec, headerHeightSpec);
238 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700239
Selim Cinekb5605e52015-02-20 18:21:41 +0100240 setMeasuredDimension(width, height);
241 }
242
Selim Cineka69f2a62015-12-11 17:28:12 -0800243 @Override
Selim Cinek0242fbb2016-10-19 13:38:32 -0700244 public boolean hasOverlappingRendering() {
245 return false;
246 }
247
248 @Override
Selim Cineka69f2a62015-12-11 17:28:12 -0800249 public boolean pointInView(float localX, float localY, float slop) {
250 return localX >= -slop && localY >= -slop && localX < ((mRight - mLeft) + slop) &&
251 localY < (mRealHeight + slop);
252 }
253
Selim Cinekb5605e52015-02-20 18:21:41 +0100254 /**
255 * Add a child notification to this view.
256 *
257 * @param row the row to add
258 * @param childIndex the index to add it at, if -1 it will be added at the end
259 */
260 public void addNotification(ExpandableNotificationRow row, int childIndex) {
261 int newIndex = childIndex < 0 ? mChildren.size() : childIndex;
262 mChildren.add(newIndex, row);
263 addView(row);
Selim Cinek8e0c4982016-02-29 20:03:08 -0800264 row.setUserLocked(mUserLocked);
Selim Cinek7b836392015-12-04 20:02:59 -0800265
266 View divider = inflateDivider();
267 addView(divider);
268 mDividers.add(newIndex, divider);
Selim Cinekc897bd32016-03-18 17:32:31 -0700269
270 updateGroupOverflow();
Selim Cinek2b549f42016-11-22 16:38:51 -0800271 row.setContentTransformationAmount(0, false /* isLastChild */);
Selim Cinekf93bf3e2018-05-08 14:43:21 -0700272 // It doesn't make sense to keep old animations around, lets cancel them!
273 ExpandableNotificationRow.NotificationViewState viewState = row.getViewState();
274 if (viewState != null) {
275 viewState.cancelAnimations(row);
276 row.cancelAppearDrawing();
277 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100278 }
279
280 public void removeNotification(ExpandableNotificationRow row) {
281 int childIndex = mChildren.indexOf(row);
282 mChildren.remove(row);
283 removeView(row);
Selim Cinek7b836392015-12-04 20:02:59 -0800284
Selim Cinek7b73a4c2016-01-12 18:32:11 -0800285 final View divider = mDividers.remove(childIndex);
Selim Cinek7b836392015-12-04 20:02:59 -0800286 removeView(divider);
Selim Cinek7b73a4c2016-01-12 18:32:11 -0800287 getOverlay().add(divider);
288 CrossFadeHelper.fadeOut(divider, new Runnable() {
289 @Override
290 public void run() {
291 getOverlay().remove(divider);
292 }
293 });
Selim Cinek7b836392015-12-04 20:02:59 -0800294
Selim Cinekb5605e52015-02-20 18:21:41 +0100295 row.setSystemChildExpanded(false);
Selim Cinek8e0c4982016-02-29 20:03:08 -0800296 row.setUserLocked(false);
Selim Cinekc897bd32016-03-18 17:32:31 -0700297 updateGroupOverflow();
Mady Mellorb0a82462016-04-30 17:31:02 -0700298 if (!row.isRemoved()) {
299 mHeaderUtil.restoreNotificationHeader(row);
300 }
301 }
302
303 /**
304 * @return The number of notification children in the container.
305 */
306 public int getNotificationChildCount() {
307 return mChildren.size();
308 }
309
Selim Cinek414ad332017-02-24 19:06:12 -0800310 public void recreateNotificationHeader(OnClickListener listener) {
311 mHeaderClickListener = listener;
312 StatusBarNotification notification = mContainingNotification.getStatusBarNotification();
Mady Mellorb0a82462016-04-30 17:31:02 -0700313 final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(),
Selim Cinek414ad332017-02-24 19:06:12 -0800314 notification.getNotification());
Adrian Roos6f6e1592017-05-02 16:22:53 -0700315 RemoteViews header = builder.makeNotificationHeader(false /* ambient */);
Mady Mellorb0a82462016-04-30 17:31:02 -0700316 if (mNotificationHeader == null) {
317 mNotificationHeader = (NotificationHeaderView) header.apply(getContext(), this);
318 final View expandButton = mNotificationHeader.findViewById(
319 com.android.internal.R.id.expand_button);
320 expandButton.setVisibility(VISIBLE);
Selim Cinek414ad332017-02-24 19:06:12 -0800321 mNotificationHeader.setOnClickListener(mHeaderClickListener);
Mady Mellorb0a82462016-04-30 17:31:02 -0700322 mNotificationHeaderWrapper = NotificationViewWrapper.wrap(getContext(),
Selim Cinek414ad332017-02-24 19:06:12 -0800323 mNotificationHeader, mContainingNotification);
Mady Mellorb0a82462016-04-30 17:31:02 -0700324 addView(mNotificationHeader, 0);
325 invalidate();
326 } else {
327 header.reapply(getContext(), mNotificationHeader);
Mady Mellorb0a82462016-04-30 17:31:02 -0700328 }
Selim Cinek131f1a42017-06-05 17:50:19 -0700329 mNotificationHeaderWrapper.onContentUpdated(mContainingNotification);
Selim Cinek414ad332017-02-24 19:06:12 -0800330 recreateLowPriorityHeader(builder);
Adrian Roos6f6e1592017-05-02 16:22:53 -0700331 recreateAmbientHeader(builder);
Lucas Dupin6ea27872017-05-30 12:00:04 -0700332 updateHeaderVisibility(false /* animate */);
Mady Mellorb0a82462016-04-30 17:31:02 -0700333 updateChildrenHeaderAppearance();
334 }
335
Adrian Roos6f6e1592017-05-02 16:22:53 -0700336 private void recreateAmbientHeader(Notification.Builder builder) {
337 RemoteViews header;
338 StatusBarNotification notification = mContainingNotification.getStatusBarNotification();
339 if (builder == null) {
340 builder = Notification.Builder.recoverBuilder(getContext(),
341 notification.getNotification());
342 }
343 header = builder.makeNotificationHeader(true /* ambient */);
344 if (mNotificationHeaderAmbient == null) {
345 mNotificationHeaderAmbient = (ViewGroup) header.apply(getContext(), this);
346 mNotificationHeaderWrapperAmbient = NotificationViewWrapper.wrap(getContext(),
347 mNotificationHeaderAmbient, mContainingNotification);
Selim Cinek131f1a42017-06-05 17:50:19 -0700348 mNotificationHeaderWrapperAmbient.onContentUpdated(mContainingNotification);
Adrian Roos6f6e1592017-05-02 16:22:53 -0700349 addView(mNotificationHeaderAmbient, 0);
350 invalidate();
351 } else {
352 header.reapply(getContext(), mNotificationHeaderAmbient);
353 }
354 resetHeaderVisibilityIfNeeded(mNotificationHeaderAmbient, calculateDesiredHeader());
Selim Cinek131f1a42017-06-05 17:50:19 -0700355 mNotificationHeaderWrapperAmbient.onContentUpdated(mContainingNotification);
Adrian Roos6f6e1592017-05-02 16:22:53 -0700356 }
357
Selim Cinek414ad332017-02-24 19:06:12 -0800358 /**
359 * Recreate the low-priority header.
360 *
361 * @param builder a builder to reuse. Otherwise the builder will be recovered.
362 */
363 private void recreateLowPriorityHeader(Notification.Builder builder) {
364 RemoteViews header;
365 StatusBarNotification notification = mContainingNotification.getStatusBarNotification();
366 if (mIsLowPriority) {
367 if (builder == null) {
368 builder = Notification.Builder.recoverBuilder(getContext(),
369 notification.getNotification());
370 }
371 header = builder.makeLowPriorityContentView(true /* useRegularSubtext */);
372 if (mNotificationHeaderLowPriority == null) {
373 mNotificationHeaderLowPriority = (NotificationHeaderView) header.apply(getContext(),
374 this);
375 final View expandButton = mNotificationHeaderLowPriority.findViewById(
376 com.android.internal.R.id.expand_button);
377 expandButton.setVisibility(VISIBLE);
378 mNotificationHeaderLowPriority.setOnClickListener(mHeaderClickListener);
379 mNotificationHeaderWrapperLowPriority = NotificationViewWrapper.wrap(getContext(),
380 mNotificationHeaderLowPriority, mContainingNotification);
381 addView(mNotificationHeaderLowPriority, 0);
382 invalidate();
383 } else {
384 header.reapply(getContext(), mNotificationHeaderLowPriority);
385 }
Selim Cinek131f1a42017-06-05 17:50:19 -0700386 mNotificationHeaderWrapperLowPriority.onContentUpdated(mContainingNotification);
Adrian Roos6f6e1592017-05-02 16:22:53 -0700387 resetHeaderVisibilityIfNeeded(mNotificationHeaderLowPriority, calculateDesiredHeader());
Selim Cinek414ad332017-02-24 19:06:12 -0800388 } else {
Selim Cinek6fd06b52017-03-07 15:54:10 -0800389 removeView(mNotificationHeaderLowPriority);
Selim Cinek414ad332017-02-24 19:06:12 -0800390 mNotificationHeaderLowPriority = null;
391 mNotificationHeaderWrapperLowPriority = null;
392 }
393 }
394
Mady Mellorb0a82462016-04-30 17:31:02 -0700395 public void updateChildrenHeaderAppearance() {
396 mHeaderUtil.updateChildrenHeaderAppearance();
Selim Cinekc897bd32016-03-18 17:32:31 -0700397 }
398
399 public void updateGroupOverflow() {
400 int childCount = mChildren.size();
401 int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
402 if (childCount > maxAllowedVisibleChildren) {
Lucas Dupin4c797d62018-05-07 15:32:13 -0700403 int number = childCount - maxAllowedVisibleChildren;
404 mOverflowNumber = mHybridGroupManager.bindOverflowNumber(mOverflowNumber, number);
405 if (mContainingNotification.isShowingAmbient()) {
406 ExpandableNotificationRow overflowView = mChildren.get(0);
407 HybridNotificationView ambientSingleLineView = overflowView == null ? null
408 : overflowView.getAmbientSingleLineView();
409 if (ambientSingleLineView != null) {
410 mHybridGroupManager.bindOverflowNumberAmbient(
411 ambientSingleLineView.getTitleView(),
412 mContainingNotification.getStatusBarNotification().getNotification(),
413 number);
414 }
415 }
Selim Cinekc897bd32016-03-18 17:32:31 -0700416 if (mGroupOverFlowState == null) {
417 mGroupOverFlowState = new ViewState();
418 mNeverAppliedGroupState = true;
419 }
420 } else if (mOverflowNumber != null) {
421 removeView(mOverflowNumber);
Eliot Courtneybb8af1c2018-01-11 16:44:45 +0900422 if (isShown() && isAttachedToWindow()) {
Selim Cinekc897bd32016-03-18 17:32:31 -0700423 final View removedOverflowNumber = mOverflowNumber;
424 addTransientView(removedOverflowNumber, getTransientViewCount());
425 CrossFadeHelper.fadeOut(removedOverflowNumber, new Runnable() {
426 @Override
427 public void run() {
428 removeTransientView(removedOverflowNumber);
429 }
430 });
431 }
432 mOverflowNumber = null;
Selim Cinekc897bd32016-03-18 17:32:31 -0700433 mGroupOverFlowState = null;
434 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100435 }
436
Selim Cineka3d3b912016-02-02 11:22:06 -0800437 @Override
438 protected void onConfigurationChanged(Configuration newConfig) {
439 super.onConfigurationChanged(newConfig);
Selim Cinekc897bd32016-03-18 17:32:31 -0700440 updateGroupOverflow();
Selim Cineka3d3b912016-02-02 11:22:06 -0800441 }
442
Selim Cinekb5605e52015-02-20 18:21:41 +0100443 private View inflateDivider() {
444 return LayoutInflater.from(mContext).inflate(
445 R.layout.notification_children_divider, this, false);
446 }
447
448 public List<ExpandableNotificationRow> getNotificationChildren() {
449 return mChildren;
450 }
451
452 /**
453 * Apply the order given in the list to the children.
454 *
455 * @param childOrder the new list order
Selim Cineka7d4f822016-12-06 14:34:47 -0800456 * @param visualStabilityManager
457 * @param callback
Selim Cinekb5605e52015-02-20 18:21:41 +0100458 * @return whether the list order has changed
459 */
Selim Cineka7d4f822016-12-06 14:34:47 -0800460 public boolean applyChildOrder(List<ExpandableNotificationRow> childOrder,
461 VisualStabilityManager visualStabilityManager,
462 VisualStabilityManager.Callback callback) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100463 if (childOrder == null) {
464 return false;
465 }
466 boolean result = false;
467 for (int i = 0; i < mChildren.size() && i < childOrder.size(); i++) {
468 ExpandableNotificationRow child = mChildren.get(i);
469 ExpandableNotificationRow desiredChild = childOrder.get(i);
470 if (child != desiredChild) {
Selim Cineka7d4f822016-12-06 14:34:47 -0800471 if (visualStabilityManager.canReorderNotification(desiredChild)) {
472 mChildren.remove(desiredChild);
473 mChildren.add(i, desiredChild);
474 result = true;
475 } else {
476 visualStabilityManager.addReorderingAllowedCallback(callback);
477 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100478 }
479 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700480 updateExpansionStates();
Selim Cinekb5605e52015-02-20 18:21:41 +0100481 return result;
482 }
483
Selim Cinek83bc7832015-10-22 13:26:54 -0700484 private void updateExpansionStates() {
Selim Cinek898d1732016-02-29 19:57:35 -0800485 if (mChildrenExpanded || mUserLocked) {
486 // we don't modify it the group is expanded or if we are expanding it
487 return;
488 }
489 int size = mChildren.size();
490 for (int i = 0; i < size; i++) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700491 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinek898d1732016-02-29 19:57:35 -0800492 child.setSystemChildExpanded(i == 0 && size == 1);
Selim Cinek83bc7832015-10-22 13:26:54 -0700493 }
494 }
495
496 /**
497 *
498 * @return the intrinsic size of this children container, i.e the natural fully expanded state
499 */
Selim Cinekb5605e52015-02-20 18:21:41 +0100500 public int getIntrinsicHeight() {
Selim Cinek83bc7832015-10-22 13:26:54 -0700501 int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren();
502 return getIntrinsicHeight(maxAllowedVisibleChildren);
503 }
504
505 /**
506 * @return the intrinsic height with a number of children given
507 * in @param maxAllowedVisibleChildren
508 */
509 private int getIntrinsicHeight(float maxAllowedVisibleChildren) {
Selim Cinek414ad332017-02-24 19:06:12 -0800510 if (showingAsLowPriority()) {
511 return mNotificationHeaderLowPriority.getHeight();
512 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800513 int intrinsicHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation;
Selim Cinekb5605e52015-02-20 18:21:41 +0100514 int visibleChildren = 0;
Selim Cinek83bc7832015-10-22 13:26:54 -0700515 int childCount = mChildren.size();
Selim Cinek42357e02016-02-24 18:48:01 -0800516 boolean firstChild = true;
517 float expandFactor = 0;
518 if (mUserLocked) {
Selim Cinekf07d0622016-03-21 19:52:52 -0700519 expandFactor = getGroupExpandFraction();
Selim Cinek42357e02016-02-24 18:48:01 -0800520 }
Adrian Roos6f6e1592017-05-02 16:22:53 -0700521 boolean childrenExpanded = mChildrenExpanded || mContainingNotification.isShowingAmbient();
Selim Cinekb5605e52015-02-20 18:21:41 +0100522 for (int i = 0; i < childCount; i++) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700523 if (visibleChildren >= maxAllowedVisibleChildren) {
524 break;
525 }
Selim Cinek42357e02016-02-24 18:48:01 -0800526 if (!firstChild) {
527 if (mUserLocked) {
528 intrinsicHeight += NotificationUtils.interpolate(mChildPadding, mDividerHeight,
529 expandFactor);
530 } else {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700531 intrinsicHeight += childrenExpanded ? mDividerHeight : mChildPadding;
Selim Cinek42357e02016-02-24 18:48:01 -0800532 }
533 } else {
534 if (mUserLocked) {
535 intrinsicHeight += NotificationUtils.interpolate(
536 0,
537 mNotificatonTopPadding + mDividerHeight,
538 expandFactor);
539 } else {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700540 intrinsicHeight += childrenExpanded
Selim Cinek42357e02016-02-24 18:48:01 -0800541 ? mNotificatonTopPadding + mDividerHeight
542 : 0;
543 }
544 firstChild = false;
545 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100546 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinekb5605e52015-02-20 18:21:41 +0100547 intrinsicHeight += child.getIntrinsicHeight();
548 visibleChildren++;
549 }
Selim Cinek42357e02016-02-24 18:48:01 -0800550 if (mUserLocked) {
551 intrinsicHeight += NotificationUtils.interpolate(mCollapsedBottompadding, 0.0f,
552 expandFactor);
Adrian Roos6f6e1592017-05-02 16:22:53 -0700553 } else if (!childrenExpanded) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700554 intrinsicHeight += mCollapsedBottompadding;
555 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100556 return intrinsicHeight;
557 }
558
559 /**
560 * Update the state of all its children based on a linear layout algorithm.
Selim Cinekc25989e2018-02-16 16:42:14 -0800561 * @param resultState the state to update
Selim Cinekb5605e52015-02-20 18:21:41 +0100562 * @param parentState the state of the parent
Selim Cinekc25989e2018-02-16 16:42:14 -0800563 * @param ambientState
Selim Cinekb5605e52015-02-20 18:21:41 +0100564 */
Selim Cinekc25989e2018-02-16 16:42:14 -0800565 public void getState(StackScrollState resultState, ExpandableViewState parentState,
566 AmbientState ambientState) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100567 int childCount = mChildren.size();
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800568 int yPosition = mNotificationHeaderMargin + mCurrentHeaderTranslation;
Selim Cinekb5605e52015-02-20 18:21:41 +0100569 boolean firstChild = true;
Selim Cinek83bc7832015-10-22 13:26:54 -0700570 int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren();
Selim Cinek91c2a152016-03-17 15:01:38 -0700571 int lastVisibleIndex = maxAllowedVisibleChildren - 1;
Selim Cinekf07d0622016-03-21 19:52:52 -0700572 int firstOverflowIndex = lastVisibleIndex + 1;
Selim Cinek42357e02016-02-24 18:48:01 -0800573 float expandFactor = 0;
Selim Cinek414ad332017-02-24 19:06:12 -0800574 boolean expandingToExpandedGroup = mUserLocked && !showingAsLowPriority();
Selim Cinek42357e02016-02-24 18:48:01 -0800575 if (mUserLocked) {
Selim Cinekf07d0622016-03-21 19:52:52 -0700576 expandFactor = getGroupExpandFraction();
577 firstOverflowIndex = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
Selim Cinek42357e02016-02-24 18:48:01 -0800578 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700579
Adrian Roos16fe4952017-05-18 16:10:21 -0700580 boolean childrenExpandedAndNotAnimating = mChildrenExpanded
581 && !mContainingNotification.isGroupExpansionChanging();
Selim Cinekc25989e2018-02-16 16:42:14 -0800582 int launchTransitionCompensation = 0;
Selim Cinekb5605e52015-02-20 18:21:41 +0100583 for (int i = 0; i < childCount; i++) {
584 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinekb5605e52015-02-20 18:21:41 +0100585 if (!firstChild) {
Selim Cinek414ad332017-02-24 19:06:12 -0800586 if (expandingToExpandedGroup) {
Mady Mellorb0a82462016-04-30 17:31:02 -0700587 yPosition += NotificationUtils.interpolate(mChildPadding, mDividerHeight,
588 expandFactor);
Selim Cinek42357e02016-02-24 18:48:01 -0800589 } else {
Adrian Roos16fe4952017-05-18 16:10:21 -0700590 yPosition += mChildrenExpanded ? mDividerHeight : mChildPadding;
Selim Cinek42357e02016-02-24 18:48:01 -0800591 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100592 } else {
Selim Cinek414ad332017-02-24 19:06:12 -0800593 if (expandingToExpandedGroup) {
Selim Cinek42357e02016-02-24 18:48:01 -0800594 yPosition += NotificationUtils.interpolate(
595 0,
596 mNotificatonTopPadding + mDividerHeight,
597 expandFactor);
598 } else {
Adrian Roos16fe4952017-05-18 16:10:21 -0700599 yPosition += mChildrenExpanded ? mNotificatonTopPadding + mDividerHeight : 0;
Selim Cinek42357e02016-02-24 18:48:01 -0800600 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100601 firstChild = false;
602 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700603
Selim Cinekbbcebde2016-11-09 18:28:20 -0800604 ExpandableViewState childState = resultState.getViewStateForView(child);
Selim Cinekb5605e52015-02-20 18:21:41 +0100605 int intrinsicHeight = child.getIntrinsicHeight();
Selim Cinekb3dadcc2016-11-21 17:21:13 -0800606 childState.height = intrinsicHeight;
Selim Cinekc25989e2018-02-16 16:42:14 -0800607 childState.yTranslation = yPosition + launchTransitionCompensation;
Selim Cinekb3dadcc2016-11-21 17:21:13 -0800608 childState.hidden = false;
Mady Mellorb0a82462016-04-30 17:31:02 -0700609 // When the group is expanded, the children cast the shadows rather than the parent
610 // so use the parent's elevation here.
Adrian Roos16fe4952017-05-18 16:10:21 -0700611 childState.zTranslation =
612 (childrenExpandedAndNotAnimating && mEnableShadowOnChildNotifications)
Selim Cinekc25989e2018-02-16 16:42:14 -0800613 ? parentState.zTranslation
Mady Mellorb0a82462016-04-30 17:31:02 -0700614 : 0;
Selim Cinekb5605e52015-02-20 18:21:41 +0100615 childState.dimmed = parentState.dimmed;
616 childState.dark = parentState.dark;
617 childState.hideSensitive = parentState.hideSensitive;
Selim Cinekdb167372016-11-17 15:41:17 -0800618 childState.belowSpeedBump = parentState.belowSpeedBump;
Selim Cinekb5605e52015-02-20 18:21:41 +0100619 childState.clipTopAmount = 0;
Selim Cinekf07d0622016-03-21 19:52:52 -0700620 childState.alpha = 0;
621 if (i < firstOverflowIndex) {
Selim Cinek414ad332017-02-24 19:06:12 -0800622 childState.alpha = showingAsLowPriority() ? expandFactor : 1.0f;
Selim Cinekf07d0622016-03-21 19:52:52 -0700623 } else if (expandFactor == 1.0f && i <= lastVisibleIndex) {
624 childState.alpha = (mActualHeight - childState.yTranslation) / childState.height;
625 childState.alpha = Math.max(0.0f, Math.min(1.0f, childState.alpha));
626 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100627 childState.location = parentState.location;
Selim Cineka7d4f822016-12-06 14:34:47 -0800628 childState.inShelf = parentState.inShelf;
Selim Cinekb5605e52015-02-20 18:21:41 +0100629 yPosition += intrinsicHeight;
Selim Cinekc25989e2018-02-16 16:42:14 -0800630 if (child.isExpandAnimationRunning()) {
631 launchTransitionCompensation = -ambientState.getExpandAnimationTopChange();
632 }
Selim Cineka7d4f822016-12-06 14:34:47 -0800633
Selim Cinekb5605e52015-02-20 18:21:41 +0100634 }
Selim Cinekc897bd32016-03-18 17:32:31 -0700635 if (mOverflowNumber != null) {
636 ExpandableNotificationRow overflowView = mChildren.get(Math.min(
Lucas Dupin4c797d62018-05-07 15:32:13 -0700637 getMaxAllowedVisibleChildren(true /* likeCollapsed */), childCount) - 1);
Selim Cinekc897bd32016-03-18 17:32:31 -0700638 mGroupOverFlowState.copyFrom(resultState.getViewStateForView(overflowView));
Adrian Roos6f6e1592017-05-02 16:22:53 -0700639
Lucas Dupin4c797d62018-05-07 15:32:13 -0700640 if (mContainingNotification.isShowingAmbient()) {
641 mGroupOverFlowState.alpha = 0.0f;
642 } else if (!mChildrenExpanded) {
643 HybridNotificationView alignView = overflowView.getSingleLineView();
Adrian Roos6f6e1592017-05-02 16:22:53 -0700644 if (alignView != null) {
645 View mirrorView = alignView.getTextView();
Selim Cinekc897bd32016-03-18 17:32:31 -0700646 if (mirrorView.getVisibility() == GONE) {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700647 mirrorView = alignView.getTitleView();
Selim Cinekc897bd32016-03-18 17:32:31 -0700648 }
649 if (mirrorView.getVisibility() == GONE) {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700650 mirrorView = alignView;
Selim Cinekc897bd32016-03-18 17:32:31 -0700651 }
Lucas Dupin4c797d62018-05-07 15:32:13 -0700652 mGroupOverFlowState.alpha = mirrorView.getAlpha();
Selim Cinekc897bd32016-03-18 17:32:31 -0700653 mGroupOverFlowState.yTranslation += NotificationUtils.getRelativeYOffset(
654 mirrorView, overflowView);
Selim Cinekc897bd32016-03-18 17:32:31 -0700655 }
656 } else {
Mady Mellorb0a82462016-04-30 17:31:02 -0700657 mGroupOverFlowState.yTranslation += mNotificationHeaderMargin;
Selim Cinekc897bd32016-03-18 17:32:31 -0700658 mGroupOverFlowState.alpha = 0.0f;
659 }
660 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700661 if (mNotificationHeader != null) {
662 if (mHeaderViewState == null) {
663 mHeaderViewState = new ViewState();
664 }
665 mHeaderViewState.initFrom(mNotificationHeader);
Adrian Roos16fe4952017-05-18 16:10:21 -0700666 mHeaderViewState.zTranslation = childrenExpandedAndNotAnimating
Selim Cinekc25989e2018-02-16 16:42:14 -0800667 ? parentState.zTranslation
Mady Mellorb0a82462016-04-30 17:31:02 -0700668 : 0;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800669 mHeaderViewState.yTranslation = mCurrentHeaderTranslation;
670 mHeaderViewState.alpha = mHeaderVisibleAmount;
671 // The hiding is done automatically by the alpha, otherwise we'll pick it up again
672 // in the next frame with the initFrom call above and have an invisible header
673 mHeaderViewState.hidden = false;
Mady Mellorb0a82462016-04-30 17:31:02 -0700674 }
675 }
676
677 /**
678 * When moving into the bottom stack, the bottom visible child in an expanded group adjusts its
679 * height, children in the group after this are gone.
680 *
681 * @param child the child who's height to adjust.
682 * @param parentHeight the height of the parent.
683 * @param childState the state to update.
684 * @param yPosition the yPosition of the view.
685 * @return true if children after this one should be hidden.
686 */
687 private boolean updateChildStateForExpandedGroup(ExpandableNotificationRow child,
Selim Cinekbbcebde2016-11-09 18:28:20 -0800688 int parentHeight, ExpandableViewState childState, int yPosition) {
Mady Mellorb0a82462016-04-30 17:31:02 -0700689 final int top = yPosition + child.getClipTopAmount();
690 final int intrinsicHeight = child.getIntrinsicHeight();
691 final int bottom = top + intrinsicHeight;
692 int newHeight = intrinsicHeight;
693 if (bottom >= parentHeight) {
694 // Child is either clipped or gone
695 newHeight = Math.max((parentHeight - top), 0);
696 }
697 childState.hidden = newHeight == 0;
698 childState.height = newHeight;
699 return childState.height != intrinsicHeight && !childState.hidden;
Selim Cinek83bc7832015-10-22 13:26:54 -0700700 }
701
702 private int getMaxAllowedVisibleChildren() {
703 return getMaxAllowedVisibleChildren(false /* likeCollapsed */);
704 }
705
706 private int getMaxAllowedVisibleChildren(boolean likeCollapsed) {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700707 if (mContainingNotification.isShowingAmbient()) {
708 return NUMBER_OF_CHILDREN_WHEN_AMBIENT;
709 }
Selim Cinek414ad332017-02-24 19:06:12 -0800710 if (!likeCollapsed && (mChildrenExpanded || mContainingNotification.isUserLocked())) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700711 return NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED;
712 }
Selim Cinek414ad332017-02-24 19:06:12 -0800713 if (mIsLowPriority || !mContainingNotification.isOnKeyguard()
714 && (mContainingNotification.isExpanded() || mContainingNotification.isHeadsUp())) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700715 return NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED;
716 }
717 return NUMBER_OF_CHILDREN_WHEN_COLLAPSED;
Selim Cinekb5605e52015-02-20 18:21:41 +0100718 }
719
720 public void applyState(StackScrollState state) {
721 int childCount = mChildren.size();
Selim Cinek83bc7832015-10-22 13:26:54 -0700722 ViewState tmpState = new ViewState();
Selim Cinekc897bd32016-03-18 17:32:31 -0700723 float expandFraction = 0.0f;
724 if (mUserLocked) {
Selim Cinekf07d0622016-03-21 19:52:52 -0700725 expandFraction = getGroupExpandFraction();
Selim Cinekc897bd32016-03-18 17:32:31 -0700726 }
Selim Cinek414ad332017-02-24 19:06:12 -0800727 final boolean dividersVisible = mUserLocked && !showingAsLowPriority()
Anthony Chen6bf88a02017-04-10 14:41:44 -0700728 || (mChildrenExpanded && mShowDividersWhenExpanded)
729 || (mContainingNotification.isGroupExpansionChanging()
730 && !mHideDividersDuringExpand);
Selim Cinekb5605e52015-02-20 18:21:41 +0100731 for (int i = 0; i < childCount; i++) {
732 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinekbbcebde2016-11-09 18:28:20 -0800733 ExpandableViewState viewState = state.getViewStateForView(child);
734 viewState.applyToView(child);
Selim Cinek7b836392015-12-04 20:02:59 -0800735
736 // layout the divider
737 View divider = mDividers.get(i);
738 tmpState.initFrom(divider);
739 tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
Anthony Chen6bf88a02017-04-10 14:41:44 -0700740 float alpha = mChildrenExpanded && viewState.alpha != 0 ? mDividerAlpha : 0;
Selim Cinek414ad332017-02-24 19:06:12 -0800741 if (mUserLocked && !showingAsLowPriority() && viewState.alpha != 0) {
Selim Cinekf07d0622016-03-21 19:52:52 -0700742 alpha = NotificationUtils.interpolate(0, 0.5f,
743 Math.min(viewState.alpha, expandFraction));
Selim Cinek42357e02016-02-24 18:48:01 -0800744 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700745 tmpState.hidden = !dividersVisible;
Selim Cinek42357e02016-02-24 18:48:01 -0800746 tmpState.alpha = alpha;
Selim Cinekbbcebde2016-11-09 18:28:20 -0800747 tmpState.applyToView(divider);
Selim Cinekd9a3f8f2016-03-17 19:39:00 -0700748 // There is no fake shadow to be drawn on the children
749 child.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
Selim Cinekb5605e52015-02-20 18:21:41 +0100750 }
Selim Cinek0cfbef42016-11-09 19:06:36 -0800751 if (mGroupOverFlowState != null) {
Selim Cinekbbcebde2016-11-09 18:28:20 -0800752 mGroupOverFlowState.applyToView(mOverflowNumber);
Selim Cinekc897bd32016-03-18 17:32:31 -0700753 mNeverAppliedGroupState = false;
754 }
Selim Cinek0cfbef42016-11-09 19:06:36 -0800755 if (mHeaderViewState != null) {
Selim Cinekbbcebde2016-11-09 18:28:20 -0800756 mHeaderViewState.applyToView(mNotificationHeader);
Mady Mellorb0a82462016-04-30 17:31:02 -0700757 }
Selim Cinekb3dadcc2016-11-21 17:21:13 -0800758 updateChildrenClipping();
759 }
760
761 private void updateChildrenClipping() {
Selim Cinekc25989e2018-02-16 16:42:14 -0800762 if (mContainingNotification.hasExpandingChild()) {
763 return;
764 }
Selim Cinekb3dadcc2016-11-21 17:21:13 -0800765 int childCount = mChildren.size();
Selim Cinek414ad332017-02-24 19:06:12 -0800766 int layoutEnd = mContainingNotification.getActualHeight() - mClipBottomAmount;
Selim Cinekb3dadcc2016-11-21 17:21:13 -0800767 for (int i = 0; i < childCount; i++) {
768 ExpandableNotificationRow child = mChildren.get(i);
769 if (child.getVisibility() == GONE) {
770 continue;
771 }
772 float childTop = child.getTranslationY();
773 float childBottom = childTop + child.getActualHeight();
774 boolean visible = true;
775 int clipBottomAmount = 0;
776 if (childTop > layoutEnd) {
777 visible = false;
778 } else if (childBottom > layoutEnd) {
779 clipBottomAmount = (int) (childBottom - layoutEnd);
780 }
781
782 boolean isVisible = child.getVisibility() == VISIBLE;
783 if (visible != isVisible) {
784 child.setVisibility(visible ? VISIBLE : INVISIBLE);
785 }
786
787 child.setClipBottomAmount(clipBottomAmount);
788 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100789 }
790
Selim Cinekb5605e52015-02-20 18:21:41 +0100791 /**
792 * This is called when the children expansion has changed and positions the children properly
793 * for an appear animation.
794 *
795 * @param state the new state we animate to
796 */
797 public void prepareExpansionChanged(StackScrollState state) {
Selim Cinek277a8aa2016-01-22 12:12:37 -0800798 // TODO: do something that makes sense, like placing the invisible views correctly
799 return;
Selim Cinekb5605e52015-02-20 18:21:41 +0100800 }
801
Selim Cinek0cfbef42016-11-09 19:06:36 -0800802 public void startAnimationToState(StackScrollState state, AnimationProperties properties) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100803 int childCount = mChildren.size();
Selim Cinek83bc7832015-10-22 13:26:54 -0700804 ViewState tmpState = new ViewState();
Selim Cinekf07d0622016-03-21 19:52:52 -0700805 float expandFraction = getGroupExpandFraction();
Selim Cinek414ad332017-02-24 19:06:12 -0800806 final boolean dividersVisible = mUserLocked && !showingAsLowPriority()
Anthony Chen6bf88a02017-04-10 14:41:44 -0700807 || (mChildrenExpanded && mShowDividersWhenExpanded)
808 || (mContainingNotification.isGroupExpansionChanging()
809 && !mHideDividersDuringExpand);
Selim Cineke8126522015-12-08 21:21:17 -0800810 for (int i = childCount - 1; i >= 0; i--) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100811 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinekbbcebde2016-11-09 18:28:20 -0800812 ExpandableViewState viewState = state.getViewStateForView(child);
Selim Cinek0cfbef42016-11-09 19:06:36 -0800813 viewState.animateTo(child, properties);
Selim Cinek7b836392015-12-04 20:02:59 -0800814
815 // layout the divider
816 View divider = mDividers.get(i);
817 tmpState.initFrom(divider);
818 tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
Selim Cinek42357e02016-02-24 18:48:01 -0800819 float alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
Selim Cinek414ad332017-02-24 19:06:12 -0800820 if (mUserLocked && !showingAsLowPriority() && viewState.alpha != 0) {
Selim Cinekf07d0622016-03-21 19:52:52 -0700821 alpha = NotificationUtils.interpolate(0, 0.5f,
822 Math.min(viewState.alpha, expandFraction));
Selim Cinek42357e02016-02-24 18:48:01 -0800823 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700824 tmpState.hidden = !dividersVisible;
Selim Cinek42357e02016-02-24 18:48:01 -0800825 tmpState.alpha = alpha;
Selim Cinek0cfbef42016-11-09 19:06:36 -0800826 tmpState.animateTo(divider, properties);
Selim Cinekd9a3f8f2016-03-17 19:39:00 -0700827 // There is no fake shadow to be drawn on the children
828 child.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
Selim Cinekb5605e52015-02-20 18:21:41 +0100829 }
Selim Cinekc897bd32016-03-18 17:32:31 -0700830 if (mOverflowNumber != null) {
831 if (mNeverAppliedGroupState) {
832 float alpha = mGroupOverFlowState.alpha;
833 mGroupOverFlowState.alpha = 0;
Selim Cinekbbcebde2016-11-09 18:28:20 -0800834 mGroupOverFlowState.applyToView(mOverflowNumber);
Selim Cinekc897bd32016-03-18 17:32:31 -0700835 mGroupOverFlowState.alpha = alpha;
836 mNeverAppliedGroupState = false;
837 }
Selim Cinek0cfbef42016-11-09 19:06:36 -0800838 mGroupOverFlowState.animateTo(mOverflowNumber, properties);
Selim Cinekc897bd32016-03-18 17:32:31 -0700839 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700840 if (mNotificationHeader != null) {
Selim Cinekbbcebde2016-11-09 18:28:20 -0800841 mHeaderViewState.applyToView(mNotificationHeader);
Mady Mellorb0a82462016-04-30 17:31:02 -0700842 }
Selim Cinekb3dadcc2016-11-21 17:21:13 -0800843 updateChildrenClipping();
Selim Cinekb5605e52015-02-20 18:21:41 +0100844 }
845
846 public ExpandableNotificationRow getViewAtPosition(float y) {
847 // find the view under the pointer, accounting for GONE views
848 final int count = mChildren.size();
849 for (int childIdx = 0; childIdx < count; childIdx++) {
850 ExpandableNotificationRow slidingChild = mChildren.get(childIdx);
851 float childTop = slidingChild.getTranslationY();
852 float top = childTop + slidingChild.getClipTopAmount();
853 float bottom = childTop + slidingChild.getActualHeight();
854 if (y >= top && y <= bottom) {
855 return slidingChild;
856 }
857 }
858 return null;
859 }
860
Selim Cinek83bc7832015-10-22 13:26:54 -0700861 public void setChildrenExpanded(boolean childrenExpanded) {
862 mChildrenExpanded = childrenExpanded;
Selim Cinek898d1732016-02-29 19:57:35 -0800863 updateExpansionStates();
Mady Mellorb0a82462016-04-30 17:31:02 -0700864 if (mNotificationHeader != null) {
865 mNotificationHeader.setExpanded(childrenExpanded);
866 }
Selim Cinekddf1b392016-05-27 16:33:10 -0700867 final int count = mChildren.size();
868 for (int childIdx = 0; childIdx < count; childIdx++) {
869 ExpandableNotificationRow child = mChildren.get(childIdx);
870 child.setChildrenExpanded(childrenExpanded, false);
871 }
Selim Cinekffd3dc62018-11-30 18:06:57 -0800872 updateHeaderTouchability();
Selim Cinek83bc7832015-10-22 13:26:54 -0700873 }
874
Selim Cinek414ad332017-02-24 19:06:12 -0800875 public void setContainingNotification(ExpandableNotificationRow parent) {
876 mContainingNotification = parent;
877 mHeaderUtil = new NotificationHeaderUtil(mContainingNotification);
Mady Mellorb0a82462016-04-30 17:31:02 -0700878 }
879
Selim Cinek414ad332017-02-24 19:06:12 -0800880 public ExpandableNotificationRow getContainingNotification() {
881 return mContainingNotification;
Adrian Roos4a579672016-05-24 16:54:37 -0700882 }
883
Mady Mellorb0a82462016-04-30 17:31:02 -0700884 public NotificationHeaderView getHeaderView() {
885 return mNotificationHeader;
886 }
887
Selim Cinek414ad332017-02-24 19:06:12 -0800888 public NotificationHeaderView getLowPriorityHeaderView() {
889 return mNotificationHeaderLowPriority;
890 }
891
Lucas Dupin6ea27872017-05-30 12:00:04 -0700892 @VisibleForTesting
893 public ViewGroup getCurrentHeaderView() {
894 return mCurrentHeader;
895 }
896
Adrian Roos6f6e1592017-05-02 16:22:53 -0700897 public void notifyShowAmbientChanged() {
898 updateHeaderVisibility(false);
Lucas Dupin4c797d62018-05-07 15:32:13 -0700899 updateGroupOverflow();
Adrian Roos6f6e1592017-05-02 16:22:53 -0700900 }
901
Selim Cinek414ad332017-02-24 19:06:12 -0800902 private void updateHeaderVisibility(boolean animate) {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700903 ViewGroup desiredHeader;
904 ViewGroup currentHeader = mCurrentHeader;
905 desiredHeader = calculateDesiredHeader();
906
907 if (currentHeader == desiredHeader) {
908 return;
Mady Mellorb0a82462016-04-30 17:31:02 -0700909 }
Adrian Roos6f6e1592017-05-02 16:22:53 -0700910 if (desiredHeader == mNotificationHeaderAmbient
911 || currentHeader == mNotificationHeaderAmbient) {
912 animate = false;
913 }
914
Selim Cinek414ad332017-02-24 19:06:12 -0800915 if (animate) {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700916 if (desiredHeader != null && currentHeader != null) {
917 currentHeader.setVisibility(VISIBLE);
918 desiredHeader.setVisibility(VISIBLE);
919 NotificationViewWrapper visibleWrapper = getWrapperForView(desiredHeader);
920 NotificationViewWrapper hiddenWrapper = getWrapperForView(currentHeader);
Selim Cinek414ad332017-02-24 19:06:12 -0800921 visibleWrapper.transformFrom(hiddenWrapper);
922 hiddenWrapper.transformTo(visibleWrapper, () -> updateHeaderVisibility(false));
Adrian Roos6f6e1592017-05-02 16:22:53 -0700923 startChildAlphaAnimations(desiredHeader == mNotificationHeader);
Selim Cinek414ad332017-02-24 19:06:12 -0800924 } else {
925 animate = false;
926 }
927 }
928 if (!animate) {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700929 if (desiredHeader != null) {
930 getWrapperForView(desiredHeader).setVisible(true);
931 desiredHeader.setVisibility(VISIBLE);
Selim Cinek414ad332017-02-24 19:06:12 -0800932 }
Adrian Roos6f6e1592017-05-02 16:22:53 -0700933 if (currentHeader != null) {
Lucas Dupin6ea27872017-05-30 12:00:04 -0700934 // Wrapper can be null if we were a low priority notification
935 // and just destroyed it by calling setIsLowPriority(false)
936 NotificationViewWrapper wrapper = getWrapperForView(currentHeader);
937 if (wrapper != null) {
938 wrapper.setVisible(false);
939 }
Adrian Roos6f6e1592017-05-02 16:22:53 -0700940 currentHeader.setVisibility(INVISIBLE);
Selim Cinek414ad332017-02-24 19:06:12 -0800941 }
942 }
Adrian Roos6f6e1592017-05-02 16:22:53 -0700943
944 resetHeaderVisibilityIfNeeded(mNotificationHeader, desiredHeader);
945 resetHeaderVisibilityIfNeeded(mNotificationHeaderAmbient, desiredHeader);
946 resetHeaderVisibilityIfNeeded(mNotificationHeaderLowPriority, desiredHeader);
947
Lucas Dupin6ea27872017-05-30 12:00:04 -0700948 mCurrentHeader = desiredHeader;
Adrian Roos6f6e1592017-05-02 16:22:53 -0700949 }
950
951 private void resetHeaderVisibilityIfNeeded(View header, View desiredHeader) {
952 if (header == null) {
953 return;
954 }
955 if (header != mCurrentHeader && header != desiredHeader) {
956 getWrapperForView(header).setVisible(false);
957 header.setVisibility(INVISIBLE);
958 }
959 if (header == desiredHeader && header.getVisibility() != VISIBLE) {
960 getWrapperForView(header).setVisible(true);
961 header.setVisibility(VISIBLE);
962 }
963 }
964
965 private ViewGroup calculateDesiredHeader() {
966 ViewGroup desiredHeader;
967 if (mContainingNotification.isShowingAmbient()) {
968 desiredHeader = mNotificationHeaderAmbient;
969 } else if (showingAsLowPriority()) {
970 desiredHeader = mNotificationHeaderLowPriority;
971 } else {
972 desiredHeader = mNotificationHeader;
973 }
974 return desiredHeader;
Selim Cinek414ad332017-02-24 19:06:12 -0800975 }
976
977 private void startChildAlphaAnimations(boolean toVisible) {
978 float target = toVisible ? 1.0f : 0.0f;
979 float start = 1.0f - target;
980 int childCount = mChildren.size();
981 for (int i = 0; i < childCount; i++) {
982 if (i >= NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED) {
983 break;
984 }
985 ExpandableNotificationRow child = mChildren.get(i);
986 child.setAlpha(start);
987 ViewState viewState = new ViewState();
988 viewState.initFrom(child);
989 viewState.alpha = target;
990 ALPHA_FADE_IN.setDelay(i * 50);
991 viewState.animateTo(child, ALPHA_FADE_IN);
992 }
993 }
994
995
996 private void updateHeaderTransformation() {
Selim Cinek1826d982017-03-06 20:47:37 -0800997 if (mUserLocked && showingAsLowPriority()) {
Selim Cinek414ad332017-02-24 19:06:12 -0800998 float fraction = getGroupExpandFraction();
999 mNotificationHeaderWrapper.transformFrom(mNotificationHeaderWrapperLowPriority,
1000 fraction);
1001 mNotificationHeader.setVisibility(VISIBLE);
1002 mNotificationHeaderWrapperLowPriority.transformTo(mNotificationHeaderWrapper,
1003 fraction);
1004 }
1005
1006 }
1007
Adrian Roos6f6e1592017-05-02 16:22:53 -07001008 private NotificationViewWrapper getWrapperForView(View visibleHeader) {
Selim Cinek414ad332017-02-24 19:06:12 -08001009 if (visibleHeader == mNotificationHeader) {
1010 return mNotificationHeaderWrapper;
1011 }
Adrian Roos6f6e1592017-05-02 16:22:53 -07001012 if (visibleHeader == mNotificationHeaderAmbient) {
1013 return mNotificationHeaderWrapperAmbient;
1014 }
Selim Cinek414ad332017-02-24 19:06:12 -08001015 return mNotificationHeaderWrapperLowPriority;
Mady Mellorb0a82462016-04-30 17:31:02 -07001016 }
1017
1018 /**
1019 * Called when a groups expansion changes to adjust the background of the header view.
1020 *
1021 * @param expanded whether the group is expanded.
1022 */
1023 public void updateHeaderForExpansion(boolean expanded) {
1024 if (mNotificationHeader != null) {
1025 if (expanded) {
1026 ColorDrawable cd = new ColorDrawable();
Selim Cinek414ad332017-02-24 19:06:12 -08001027 cd.setColor(mContainingNotification.calculateBgColor());
Mady Mellorb0a82462016-04-30 17:31:02 -07001028 mNotificationHeader.setHeaderBackgroundDrawable(cd);
1029 } else {
1030 mNotificationHeader.setHeaderBackgroundDrawable(null);
1031 }
1032 }
Selim Cinek388df6d2015-10-22 13:25:11 -07001033 }
1034
Selim Cinek83bc7832015-10-22 13:26:54 -07001035 public int getMaxContentHeight() {
Selim Cinek414ad332017-02-24 19:06:12 -08001036 if (showingAsLowPriority()) {
1037 return getMinHeight(NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED, true
1038 /* likeHighPriority */);
1039 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001040 int maxContentHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation
1041 + mNotificatonTopPadding;
Selim Cinek42357e02016-02-24 18:48:01 -08001042 int visibleChildren = 0;
1043 int childCount = mChildren.size();
1044 for (int i = 0; i < childCount; i++) {
1045 if (visibleChildren >= NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED) {
1046 break;
1047 }
1048 ExpandableNotificationRow child = mChildren.get(i);
Selim Cineke81b82b2016-03-04 11:22:28 -08001049 float childHeight = child.isExpanded(true /* allowOnKeyguard */)
Selim Cinek42357e02016-02-24 18:48:01 -08001050 ? child.getMaxExpandHeight()
1051 : child.getShowingLayout().getMinHeight(true /* likeGroupExpanded */);
1052 maxContentHeight += childHeight;
1053 visibleChildren++;
1054 }
1055 if (visibleChildren > 0) {
1056 maxContentHeight += visibleChildren * mDividerHeight;
1057 }
1058 return maxContentHeight;
1059 }
1060
1061 public void setActualHeight(int actualHeight) {
1062 if (!mUserLocked) {
1063 return;
1064 }
1065 mActualHeight = actualHeight;
Selim Cinekf07d0622016-03-21 19:52:52 -07001066 float fraction = getGroupExpandFraction();
Selim Cinek414ad332017-02-24 19:06:12 -08001067 boolean showingLowPriority = showingAsLowPriority();
1068 updateHeaderTransformation();
Selim Cinekf07d0622016-03-21 19:52:52 -07001069 int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* forceCollapsed */);
Selim Cinek42357e02016-02-24 18:48:01 -08001070 int childCount = mChildren.size();
1071 for (int i = 0; i < childCount; i++) {
1072 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinek414ad332017-02-24 19:06:12 -08001073 float childHeight;
1074 if (showingLowPriority) {
1075 childHeight = child.getShowingLayout().getMinHeight(false /* likeGroupExpanded */);
1076 } else if (child.isExpanded(true /* allowOnKeyguard */)) {
1077 childHeight = child.getMaxExpandHeight();
1078 } else {
1079 childHeight = child.getShowingLayout().getMinHeight(
1080 true /* likeGroupExpanded */);
1081 }
Selim Cinekf07d0622016-03-21 19:52:52 -07001082 if (i < maxAllowedVisibleChildren) {
1083 float singleLineHeight = child.getShowingLayout().getMinHeight(
1084 false /* likeGroupExpanded */);
1085 child.setActualHeight((int) NotificationUtils.interpolate(singleLineHeight,
1086 childHeight, fraction), false);
1087 } else {
1088 child.setActualHeight((int) childHeight, false);
1089 }
Selim Cinek42357e02016-02-24 18:48:01 -08001090 }
1091 }
1092
Selim Cinekf07d0622016-03-21 19:52:52 -07001093 public float getGroupExpandFraction() {
Selim Cinek414ad332017-02-24 19:06:12 -08001094 int visibleChildrenExpandedHeight = showingAsLowPriority() ? getMaxContentHeight()
1095 : getVisibleChildrenExpandHeight();
Selim Cinek567e8452016-03-24 10:54:56 -07001096 int minExpandHeight = getCollapsedHeight();
Selim Cinekf07d0622016-03-21 19:52:52 -07001097 float factor = (mActualHeight - minExpandHeight)
1098 / (float) (visibleChildrenExpandedHeight - minExpandHeight);
Selim Cinek42357e02016-02-24 18:48:01 -08001099 return Math.max(0.0f, Math.min(1.0f, factor));
1100 }
1101
Selim Cinekf07d0622016-03-21 19:52:52 -07001102 private int getVisibleChildrenExpandHeight() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001103 int intrinsicHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation
1104 + mNotificatonTopPadding + mDividerHeight;
Selim Cinek42357e02016-02-24 18:48:01 -08001105 int visibleChildren = 0;
1106 int childCount = mChildren.size();
Selim Cinekf07d0622016-03-21 19:52:52 -07001107 int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* forceCollapsed */);
Selim Cinek42357e02016-02-24 18:48:01 -08001108 for (int i = 0; i < childCount; i++) {
Selim Cinekf07d0622016-03-21 19:52:52 -07001109 if (visibleChildren >= maxAllowedVisibleChildren) {
Selim Cinek42357e02016-02-24 18:48:01 -08001110 break;
1111 }
1112 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinekf07d0622016-03-21 19:52:52 -07001113 float childHeight = child.isExpanded(true /* allowOnKeyguard */)
1114 ? child.getMaxExpandHeight()
1115 : child.getShowingLayout().getMinHeight(true /* likeGroupExpanded */);
1116 intrinsicHeight += childHeight;
Selim Cinek42357e02016-02-24 18:48:01 -08001117 visibleChildren++;
1118 }
Selim Cinek42357e02016-02-24 18:48:01 -08001119 return intrinsicHeight;
Selim Cinek83bc7832015-10-22 13:26:54 -07001120 }
1121
1122 public int getMinHeight() {
Adrian Roos6f6e1592017-05-02 16:22:53 -07001123 return getMinHeight(mContainingNotification.isShowingAmbient()
1124 ? NUMBER_OF_CHILDREN_WHEN_AMBIENT
1125 : NUMBER_OF_CHILDREN_WHEN_COLLAPSED, false /* likeHighPriority */);
Selim Cinekb55386d2015-12-16 17:26:49 -08001126 }
1127
Selim Cinek567e8452016-03-24 10:54:56 -07001128 public int getCollapsedHeight() {
Selim Cinek414ad332017-02-24 19:06:12 -08001129 return getMinHeight(getMaxAllowedVisibleChildren(true /* forceCollapsed */),
1130 false /* likeHighPriority */);
Selim Cinek567e8452016-03-24 10:54:56 -07001131 }
1132
Selim Cinek414ad332017-02-24 19:06:12 -08001133 /**
1134 * Get the minimum Height for this group.
1135 *
1136 * @param maxAllowedVisibleChildren the number of children that should be visible
1137 * @param likeHighPriority if the height should be calculated as if it were not low priority
1138 */
1139 private int getMinHeight(int maxAllowedVisibleChildren, boolean likeHighPriority) {
1140 if (!likeHighPriority && showingAsLowPriority()) {
1141 return mNotificationHeaderLowPriority.getHeight();
Selim Cinek6743c0b2017-01-18 18:24:01 -08001142 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001143 int minExpandHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation;
Selim Cinek2c584612016-02-29 16:14:25 -08001144 int visibleChildren = 0;
1145 boolean firstChild = true;
1146 int childCount = mChildren.size();
1147 for (int i = 0; i < childCount; i++) {
1148 if (visibleChildren >= maxAllowedVisibleChildren) {
1149 break;
1150 }
1151 if (!firstChild) {
1152 minExpandHeight += mChildPadding;
1153 } else {
1154 firstChild = false;
1155 }
1156 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinekf1f270a2016-04-05 19:02:37 -07001157 minExpandHeight += child.getSingleLineView().getHeight();
Selim Cinek2c584612016-02-29 16:14:25 -08001158 visibleChildren++;
1159 }
1160 minExpandHeight += mCollapsedBottompadding;
1161 return minExpandHeight;
Selim Cinekb5605e52015-02-20 18:21:41 +01001162 }
Selim Cinek9c7712d2015-12-08 19:19:48 -08001163
Selim Cinek414ad332017-02-24 19:06:12 -08001164 public boolean showingAsLowPriority() {
1165 return mIsLowPriority && !mContainingNotification.isExpanded();
1166 }
1167
Selim Cinekc897bd32016-03-18 17:32:31 -07001168 public void setDark(boolean dark, boolean fade, long delay) {
1169 if (mOverflowNumber != null) {
Adrian Roos6f6e1592017-05-02 16:22:53 -07001170 mHybridGroupManager.setOverflowNumberDark(mOverflowNumber, dark, fade, delay);
Selim Cinekc897bd32016-03-18 17:32:31 -07001171 }
1172 }
1173
Mady Mellorb0a82462016-04-30 17:31:02 -07001174 public void reInflateViews(OnClickListener listener, StatusBarNotification notification) {
Selim Cinek414ad332017-02-24 19:06:12 -08001175 if (mNotificationHeader != null) {
1176 removeView(mNotificationHeader);
1177 mNotificationHeader = null;
1178 }
1179 if (mNotificationHeaderLowPriority != null) {
1180 removeView(mNotificationHeaderLowPriority);
1181 mNotificationHeaderLowPriority = null;
1182 }
Adrian Roos6f6e1592017-05-02 16:22:53 -07001183 if (mNotificationHeaderAmbient != null) {
1184 removeView(mNotificationHeaderAmbient);
1185 mNotificationHeaderAmbient = null;
1186 }
Selim Cinek414ad332017-02-24 19:06:12 -08001187 recreateNotificationHeader(listener);
Selim Cinek01af3342016-02-09 19:25:31 -08001188 initDimens();
1189 for (int i = 0; i < mDividers.size(); i++) {
1190 View prevDivider = mDividers.get(i);
1191 int index = indexOfChild(prevDivider);
1192 removeView(prevDivider);
1193 View divider = inflateDivider();
1194 addView(divider, index);
1195 mDividers.set(i, divider);
1196 }
Selim Cinek4bb59342016-04-08 19:29:35 -07001197 removeView(mOverflowNumber);
1198 mOverflowNumber = null;
Selim Cinek4bb59342016-04-08 19:29:35 -07001199 mGroupOverFlowState = null;
1200 updateGroupOverflow();
Selim Cinek01af3342016-02-09 19:25:31 -08001201 }
Selim Cinek42357e02016-02-24 18:48:01 -08001202
1203 public void setUserLocked(boolean userLocked) {
1204 mUserLocked = userLocked;
Selim Cinek414ad332017-02-24 19:06:12 -08001205 if (!mUserLocked) {
1206 updateHeaderVisibility(false /* animate */);
1207 }
Selim Cinek42357e02016-02-24 18:48:01 -08001208 int childCount = mChildren.size();
1209 for (int i = 0; i < childCount; i++) {
1210 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinek414ad332017-02-24 19:06:12 -08001211 child.setUserLocked(userLocked && !showingAsLowPriority());
Selim Cinek42357e02016-02-24 18:48:01 -08001212 }
Selim Cinekffd3dc62018-11-30 18:06:57 -08001213 updateHeaderTouchability();
1214 }
1215
1216 private void updateHeaderTouchability() {
1217 if (mNotificationHeader != null) {
1218 mNotificationHeader.setAcceptAllTouches(mChildrenExpanded || mUserLocked);
1219 }
Selim Cinek42357e02016-02-24 18:48:01 -08001220 }
Selim Cinekc897bd32016-03-18 17:32:31 -07001221
1222 public void onNotificationUpdated() {
1223 mHybridGroupManager.setOverflowNumberColor(mOverflowNumber,
Adrian Roos6f6e1592017-05-02 16:22:53 -07001224 mContainingNotification.getNotificationColor(),
1225 mContainingNotification.getNotificationColorAmbient());
Selim Cinekc897bd32016-03-18 17:32:31 -07001226 }
Adrian Roosd009ab12016-05-20 17:58:53 -07001227
Adrian Roos4a579672016-05-24 16:54:37 -07001228 public int getPositionInLinearLayout(View childInGroup) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001229 int position = mNotificationHeaderMargin + mCurrentHeaderTranslation
1230 + mNotificatonTopPadding;
Adrian Roos4a579672016-05-24 16:54:37 -07001231
1232 for (int i = 0; i < mChildren.size(); i++) {
1233 ExpandableNotificationRow child = mChildren.get(i);
1234 boolean notGone = child.getVisibility() != View.GONE;
1235 if (notGone) {
1236 position += mDividerHeight;
1237 }
1238 if (child == childInGroup) {
1239 return position;
1240 }
1241 if (notGone) {
1242 position += child.getIntrinsicHeight();
1243 }
1244 }
1245 return 0;
1246 }
Selim Cinek0242fbb2016-10-19 13:38:32 -07001247
1248 public void setIconsVisible(boolean iconsVisible) {
1249 if (mNotificationHeaderWrapper != null) {
1250 NotificationHeaderView header = mNotificationHeaderWrapper.getNotificationHeader();
1251 if (header != null) {
1252 header.getIcon().setForceHidden(!iconsVisible);
1253 }
1254 }
Selim Cinek414ad332017-02-24 19:06:12 -08001255 if (mNotificationHeaderWrapperLowPriority != null) {
1256 NotificationHeaderView header
1257 = mNotificationHeaderWrapperLowPriority.getNotificationHeader();
1258 if (header != null) {
1259 header.getIcon().setForceHidden(!iconsVisible);
1260 }
1261 }
Selim Cinek0242fbb2016-10-19 13:38:32 -07001262 }
Selim Cinekb3dadcc2016-11-21 17:21:13 -08001263
1264 public void setClipBottomAmount(int clipBottomAmount) {
1265 mClipBottomAmount = clipBottomAmount;
1266 updateChildrenClipping();
1267 }
Selim Cinek6743c0b2017-01-18 18:24:01 -08001268
1269 public void setIsLowPriority(boolean isLowPriority) {
1270 mIsLowPriority = isLowPriority;
Selim Cinek414ad332017-02-24 19:06:12 -08001271 if (mContainingNotification != null) { /* we're not yet set up yet otherwise */
1272 recreateLowPriorityHeader(null /* existingBuilder */);
1273 updateHeaderVisibility(false /* animate */);
1274 }
1275 if (mUserLocked) {
1276 setUserLocked(mUserLocked);
1277 }
1278 }
1279
1280 public NotificationHeaderView getVisibleHeader() {
1281 NotificationHeaderView header = mNotificationHeader;
1282 if (showingAsLowPriority()) {
1283 header = mNotificationHeaderLowPriority;
1284 }
1285 return header;
1286 }
1287
1288 public void onExpansionChanged() {
1289 if (mIsLowPriority) {
1290 if (mUserLocked) {
1291 setUserLocked(mUserLocked);
1292 }
1293 updateHeaderVisibility(true /* animate */);
1294 }
1295 }
1296
1297 public float getIncreasedPaddingAmount() {
1298 if (showingAsLowPriority()) {
1299 return 0.0f;
1300 }
1301 return getGroupExpandFraction();
Selim Cinek6743c0b2017-01-18 18:24:01 -08001302 }
Selim Cinek817abe72017-05-24 11:08:55 -07001303
1304 @VisibleForTesting
1305 public boolean isUserLocked() {
1306 return mUserLocked;
1307 }
Selim Cinek2871bef2017-11-22 08:40:00 -08001308
1309 public void setCurrentBottomRoundness(float currentBottomRoundness) {
1310 boolean last = true;
1311 for (int i = mChildren.size() - 1; i >= 0; i--) {
1312 ExpandableNotificationRow child = mChildren.get(i);
1313 if (child.getVisibility() == View.GONE) {
1314 continue;
1315 }
1316 float bottomRoundness = last ? currentBottomRoundness : 0.0f;
1317 child.setBottomRoundness(bottomRoundness, isShown() /* animate */);
1318 last = false;
1319 }
1320 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001321
1322 public void setHeaderVisibleAmount(float headerVisibleAmount) {
1323 mHeaderVisibleAmount = headerVisibleAmount;
1324 mCurrentHeaderTranslation = (int) ((1.0f - headerVisibleAmount) * mTranslationForHeader);
1325 }
Selim Cinekb5605e52015-02-20 18:21:41 +01001326}