blob: ac2a1e1e041f6ff95b8c575bae5b65a4372c696b [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;
Adrian Roos6f6e1592017-05-02 16:22:53 -070055 private static final int NUMBER_OF_CHILDREN_WHEN_AMBIENT = 3;
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;
105
Selim Cinekb5605e52015-02-20 18:21:41 +0100106 public NotificationChildrenContainer(Context context) {
107 this(context, null);
108 }
109
110 public NotificationChildrenContainer(Context context, AttributeSet attrs) {
111 this(context, attrs, 0);
112 }
113
114 public NotificationChildrenContainer(Context context, AttributeSet attrs, int defStyleAttr) {
115 this(context, attrs, defStyleAttr, 0);
116 }
117
118 public NotificationChildrenContainer(Context context, AttributeSet attrs, int defStyleAttr,
119 int defStyleRes) {
120 super(context, attrs, defStyleAttr, defStyleRes);
Selim Cinek01af3342016-02-09 19:25:31 -0800121 initDimens();
Selim Cinekc897bd32016-03-18 17:32:31 -0700122 mHybridGroupManager = new HybridGroupManager(getContext(), this);
Selim Cineked64a142018-02-06 18:06:01 -0800123 setClipChildren(false);
Selim Cinek01af3342016-02-09 19:25:31 -0800124 }
125
126 private void initDimens() {
Anthony Chen6bf88a02017-04-10 14:41:44 -0700127 Resources res = getResources();
128 mChildPadding = res.getDimensionPixelSize(R.dimen.notification_children_padding);
129 mDividerHeight = res.getDimensionPixelSize(
130 R.dimen.notification_children_container_divider_height);
131 mDividerAlpha = res.getFloat(R.dimen.notification_divider_alpha);
Anthony Chen6bf88a02017-04-10 14:41:44 -0700132 mNotificationHeaderMargin = res.getDimensionPixelSize(
133 R.dimen.notification_children_container_margin_top);
134 mNotificatonTopPadding = res.getDimensionPixelSize(
Selim Cinek7b836392015-12-04 20:02:59 -0800135 R.dimen.notification_children_container_top_padding);
Selim Cinekafeed292017-12-12 17:32:44 -0800136 mHeaderHeight = mNotificationHeaderMargin + mNotificatonTopPadding;
Anthony Chen6bf88a02017-04-10 14:41:44 -0700137 mCollapsedBottompadding = res.getDimensionPixelSize(
Selim Cineked64a142018-02-06 18:06:01 -0800138 com.android.internal.R.dimen.notification_content_margin);
Anthony Chen6bf88a02017-04-10 14:41:44 -0700139 mEnableShadowOnChildNotifications =
140 res.getBoolean(R.bool.config_enableShadowOnChildNotifications);
141 mShowDividersWhenExpanded =
142 res.getBoolean(R.bool.config_showDividersWhenGroupNotificationExpanded);
143 mHideDividersDuringExpand =
144 res.getBoolean(R.bool.config_hideDividersDuringExpand);
Selim Cinekb5605e52015-02-20 18:21:41 +0100145 }
146
147 @Override
148 protected void onLayout(boolean changed, int l, int t, int r, int b) {
Selim Cinek471e31a2015-12-11 13:39:48 -0800149 int childCount = Math.min(mChildren.size(), NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
Selim Cinekb5605e52015-02-20 18:21:41 +0100150 for (int i = 0; i < childCount; i++) {
151 View child = mChildren.get(i);
Selim Cinekfa760d42016-05-10 15:50:53 -0400152 // We need to layout all children even the GONE ones, such that the heights are
153 // calculated correctly as they are used to calculate how many we can fit on the screen
Selim Cinekc897bd32016-03-18 17:32:31 -0700154 child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
Selim Cinek7b836392015-12-04 20:02:59 -0800155 mDividers.get(i).layout(0, 0, getWidth(), mDividerHeight);
Selim Cinekb5605e52015-02-20 18:21:41 +0100156 }
Selim Cinekc897bd32016-03-18 17:32:31 -0700157 if (mOverflowNumber != null) {
Selim Cinek09b7dea2016-08-30 11:28:19 -0700158 boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
159 int left = (isRtl ? 0 : getWidth() - mOverflowNumber.getMeasuredWidth());
160 int right = left + mOverflowNumber.getMeasuredWidth();
161 mOverflowNumber.layout(left, 0, right, mOverflowNumber.getMeasuredHeight());
Selim Cinekc897bd32016-03-18 17:32:31 -0700162 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700163 if (mNotificationHeader != null) {
164 mNotificationHeader.layout(0, 0, mNotificationHeader.getMeasuredWidth(),
165 mNotificationHeader.getMeasuredHeight());
166 }
Selim Cinek414ad332017-02-24 19:06:12 -0800167 if (mNotificationHeaderLowPriority != null) {
168 mNotificationHeaderLowPriority.layout(0, 0,
169 mNotificationHeaderLowPriority.getMeasuredWidth(),
170 mNotificationHeaderLowPriority.getMeasuredHeight());
171 }
Adrian Roos6f6e1592017-05-02 16:22:53 -0700172 if (mNotificationHeaderAmbient != null) {
173 mNotificationHeaderAmbient.layout(0, 0,
174 mNotificationHeaderAmbient.getMeasuredWidth(),
175 mNotificationHeaderAmbient.getMeasuredHeight());
176 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100177 }
178
179 @Override
180 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100181 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
182 boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
183 boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST;
Selim Cineka69f2a62015-12-11 17:28:12 -0800184 int size = MeasureSpec.getSize(heightMeasureSpec);
Geoffrey Pitsch4dd50062016-12-06 16:41:22 -0500185 int newHeightSpec = heightMeasureSpec;
Selim Cinekb5605e52015-02-20 18:21:41 +0100186 if (hasFixedHeight || isHeightLimited) {
Geoffrey Pitsch4dd50062016-12-06 16:41:22 -0500187 newHeightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
Selim Cinekb5605e52015-02-20 18:21:41 +0100188 }
Selim Cinekc897bd32016-03-18 17:32:31 -0700189 int width = MeasureSpec.getSize(widthMeasureSpec);
190 if (mOverflowNumber != null) {
191 mOverflowNumber.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
192 newHeightSpec);
193 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100194 int dividerHeightSpec = MeasureSpec.makeMeasureSpec(mDividerHeight, MeasureSpec.EXACTLY);
Mady Mellorb0a82462016-04-30 17:31:02 -0700195 int height = mNotificationHeaderMargin + mNotificatonTopPadding;
Selim Cinek471e31a2015-12-11 13:39:48 -0800196 int childCount = Math.min(mChildren.size(), NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
Selim Cinekc897bd32016-03-18 17:32:31 -0700197 int collapsedChildren = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
198 int overflowIndex = childCount > collapsedChildren ? collapsedChildren - 1 : -1;
Selim Cinekb5605e52015-02-20 18:21:41 +0100199 for (int i = 0; i < childCount; i++) {
Selim Cinekc897bd32016-03-18 17:32:31 -0700200 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinekfa760d42016-05-10 15:50:53 -0400201 // We need to measure all children even the GONE ones, such that the heights are
202 // calculated correctly as they are used to calculate how many we can fit on the screen.
Selim Cinekc897bd32016-03-18 17:32:31 -0700203 boolean isOverflow = i == overflowIndex;
Selim Cinekb41b2f62016-04-26 14:03:29 -0700204 child.setSingleLineWidthIndention(isOverflow && mOverflowNumber != null
205 ? mOverflowNumber.getMeasuredWidth()
206 : 0);
Selim Cinekb5605e52015-02-20 18:21:41 +0100207 child.measure(widthMeasureSpec, newHeightSpec);
Selim Cinek7b836392015-12-04 20:02:59 -0800208 // layout the divider
209 View divider = mDividers.get(i);
210 divider.measure(widthMeasureSpec, dividerHeightSpec);
Selim Cinekfa760d42016-05-10 15:50:53 -0400211 if (child.getVisibility() != GONE) {
212 height += child.getMeasuredHeight() + mDividerHeight;
213 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100214 }
Selim Cineka69f2a62015-12-11 17:28:12 -0800215 mRealHeight = height;
216 if (heightMode != MeasureSpec.UNSPECIFIED) {
217 height = Math.min(height, size);
218 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700219
Selim Cinek414ad332017-02-24 19:06:12 -0800220 int headerHeightSpec = MeasureSpec.makeMeasureSpec(mHeaderHeight, MeasureSpec.EXACTLY);
Mady Mellorb0a82462016-04-30 17:31:02 -0700221 if (mNotificationHeader != null) {
Mady Mellorb0a82462016-04-30 17:31:02 -0700222 mNotificationHeader.measure(widthMeasureSpec, headerHeightSpec);
223 }
Selim Cinek414ad332017-02-24 19:06:12 -0800224 if (mNotificationHeaderLowPriority != null) {
225 headerHeightSpec = MeasureSpec.makeMeasureSpec(mHeaderHeight, MeasureSpec.EXACTLY);
226 mNotificationHeaderLowPriority.measure(widthMeasureSpec, headerHeightSpec);
227 }
Adrian Roos6f6e1592017-05-02 16:22:53 -0700228 if (mNotificationHeaderAmbient != null) {
229 headerHeightSpec = MeasureSpec.makeMeasureSpec(mHeaderHeight, MeasureSpec.EXACTLY);
230 mNotificationHeaderAmbient.measure(widthMeasureSpec, headerHeightSpec);
231 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700232
Selim Cinekb5605e52015-02-20 18:21:41 +0100233 setMeasuredDimension(width, height);
234 }
235
Selim Cineka69f2a62015-12-11 17:28:12 -0800236 @Override
Selim Cinek0242fbb2016-10-19 13:38:32 -0700237 public boolean hasOverlappingRendering() {
238 return false;
239 }
240
241 @Override
Selim Cineka69f2a62015-12-11 17:28:12 -0800242 public boolean pointInView(float localX, float localY, float slop) {
243 return localX >= -slop && localY >= -slop && localX < ((mRight - mLeft) + slop) &&
244 localY < (mRealHeight + slop);
245 }
246
Selim Cinekb5605e52015-02-20 18:21:41 +0100247 /**
248 * Add a child notification to this view.
249 *
250 * @param row the row to add
251 * @param childIndex the index to add it at, if -1 it will be added at the end
252 */
253 public void addNotification(ExpandableNotificationRow row, int childIndex) {
254 int newIndex = childIndex < 0 ? mChildren.size() : childIndex;
255 mChildren.add(newIndex, row);
256 addView(row);
Selim Cinek8e0c4982016-02-29 20:03:08 -0800257 row.setUserLocked(mUserLocked);
Selim Cinek7b836392015-12-04 20:02:59 -0800258
259 View divider = inflateDivider();
260 addView(divider);
261 mDividers.add(newIndex, divider);
Selim Cinekc897bd32016-03-18 17:32:31 -0700262
263 updateGroupOverflow();
Selim Cinek2b549f42016-11-22 16:38:51 -0800264 row.setContentTransformationAmount(0, false /* isLastChild */);
Selim Cinekb5605e52015-02-20 18:21:41 +0100265 }
266
267 public void removeNotification(ExpandableNotificationRow row) {
268 int childIndex = mChildren.indexOf(row);
269 mChildren.remove(row);
270 removeView(row);
Selim Cinek7b836392015-12-04 20:02:59 -0800271
Selim Cinek7b73a4c2016-01-12 18:32:11 -0800272 final View divider = mDividers.remove(childIndex);
Selim Cinek7b836392015-12-04 20:02:59 -0800273 removeView(divider);
Selim Cinek7b73a4c2016-01-12 18:32:11 -0800274 getOverlay().add(divider);
275 CrossFadeHelper.fadeOut(divider, new Runnable() {
276 @Override
277 public void run() {
278 getOverlay().remove(divider);
279 }
280 });
Selim Cinek7b836392015-12-04 20:02:59 -0800281
Selim Cinekb5605e52015-02-20 18:21:41 +0100282 row.setSystemChildExpanded(false);
Selim Cinek8e0c4982016-02-29 20:03:08 -0800283 row.setUserLocked(false);
Selim Cinekc897bd32016-03-18 17:32:31 -0700284 updateGroupOverflow();
Mady Mellorb0a82462016-04-30 17:31:02 -0700285 if (!row.isRemoved()) {
286 mHeaderUtil.restoreNotificationHeader(row);
287 }
288 }
289
290 /**
291 * @return The number of notification children in the container.
292 */
293 public int getNotificationChildCount() {
294 return mChildren.size();
295 }
296
Selim Cinek414ad332017-02-24 19:06:12 -0800297 public void recreateNotificationHeader(OnClickListener listener) {
298 mHeaderClickListener = listener;
299 StatusBarNotification notification = mContainingNotification.getStatusBarNotification();
Mady Mellorb0a82462016-04-30 17:31:02 -0700300 final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(),
Selim Cinek414ad332017-02-24 19:06:12 -0800301 notification.getNotification());
Adrian Roos6f6e1592017-05-02 16:22:53 -0700302 RemoteViews header = builder.makeNotificationHeader(false /* ambient */);
Mady Mellorb0a82462016-04-30 17:31:02 -0700303 if (mNotificationHeader == null) {
304 mNotificationHeader = (NotificationHeaderView) header.apply(getContext(), this);
305 final View expandButton = mNotificationHeader.findViewById(
306 com.android.internal.R.id.expand_button);
307 expandButton.setVisibility(VISIBLE);
Selim Cinek414ad332017-02-24 19:06:12 -0800308 mNotificationHeader.setOnClickListener(mHeaderClickListener);
Mady Mellorb0a82462016-04-30 17:31:02 -0700309 mNotificationHeaderWrapper = NotificationViewWrapper.wrap(getContext(),
Selim Cinek414ad332017-02-24 19:06:12 -0800310 mNotificationHeader, mContainingNotification);
Mady Mellorb0a82462016-04-30 17:31:02 -0700311 addView(mNotificationHeader, 0);
312 invalidate();
313 } else {
314 header.reapply(getContext(), mNotificationHeader);
Mady Mellorb0a82462016-04-30 17:31:02 -0700315 }
Selim Cinek131f1a42017-06-05 17:50:19 -0700316 mNotificationHeaderWrapper.onContentUpdated(mContainingNotification);
Selim Cinek414ad332017-02-24 19:06:12 -0800317 recreateLowPriorityHeader(builder);
Adrian Roos6f6e1592017-05-02 16:22:53 -0700318 recreateAmbientHeader(builder);
Lucas Dupin6ea27872017-05-30 12:00:04 -0700319 updateHeaderVisibility(false /* animate */);
Mady Mellorb0a82462016-04-30 17:31:02 -0700320 updateChildrenHeaderAppearance();
321 }
322
Adrian Roos6f6e1592017-05-02 16:22:53 -0700323 private void recreateAmbientHeader(Notification.Builder builder) {
324 RemoteViews header;
325 StatusBarNotification notification = mContainingNotification.getStatusBarNotification();
326 if (builder == null) {
327 builder = Notification.Builder.recoverBuilder(getContext(),
328 notification.getNotification());
329 }
330 header = builder.makeNotificationHeader(true /* ambient */);
331 if (mNotificationHeaderAmbient == null) {
332 mNotificationHeaderAmbient = (ViewGroup) header.apply(getContext(), this);
333 mNotificationHeaderWrapperAmbient = NotificationViewWrapper.wrap(getContext(),
334 mNotificationHeaderAmbient, mContainingNotification);
Selim Cinek131f1a42017-06-05 17:50:19 -0700335 mNotificationHeaderWrapperAmbient.onContentUpdated(mContainingNotification);
Adrian Roos6f6e1592017-05-02 16:22:53 -0700336 addView(mNotificationHeaderAmbient, 0);
337 invalidate();
338 } else {
339 header.reapply(getContext(), mNotificationHeaderAmbient);
340 }
341 resetHeaderVisibilityIfNeeded(mNotificationHeaderAmbient, calculateDesiredHeader());
Selim Cinek131f1a42017-06-05 17:50:19 -0700342 mNotificationHeaderWrapperAmbient.onContentUpdated(mContainingNotification);
Adrian Roos6f6e1592017-05-02 16:22:53 -0700343 }
344
Selim Cinek414ad332017-02-24 19:06:12 -0800345 /**
346 * Recreate the low-priority header.
347 *
348 * @param builder a builder to reuse. Otherwise the builder will be recovered.
349 */
350 private void recreateLowPriorityHeader(Notification.Builder builder) {
351 RemoteViews header;
352 StatusBarNotification notification = mContainingNotification.getStatusBarNotification();
353 if (mIsLowPriority) {
354 if (builder == null) {
355 builder = Notification.Builder.recoverBuilder(getContext(),
356 notification.getNotification());
357 }
358 header = builder.makeLowPriorityContentView(true /* useRegularSubtext */);
359 if (mNotificationHeaderLowPriority == null) {
360 mNotificationHeaderLowPriority = (NotificationHeaderView) header.apply(getContext(),
361 this);
362 final View expandButton = mNotificationHeaderLowPriority.findViewById(
363 com.android.internal.R.id.expand_button);
364 expandButton.setVisibility(VISIBLE);
365 mNotificationHeaderLowPriority.setOnClickListener(mHeaderClickListener);
366 mNotificationHeaderWrapperLowPriority = NotificationViewWrapper.wrap(getContext(),
367 mNotificationHeaderLowPriority, mContainingNotification);
368 addView(mNotificationHeaderLowPriority, 0);
369 invalidate();
370 } else {
371 header.reapply(getContext(), mNotificationHeaderLowPriority);
372 }
Selim Cinek131f1a42017-06-05 17:50:19 -0700373 mNotificationHeaderWrapperLowPriority.onContentUpdated(mContainingNotification);
Adrian Roos6f6e1592017-05-02 16:22:53 -0700374 resetHeaderVisibilityIfNeeded(mNotificationHeaderLowPriority, calculateDesiredHeader());
Selim Cinek414ad332017-02-24 19:06:12 -0800375 } else {
Selim Cinek6fd06b52017-03-07 15:54:10 -0800376 removeView(mNotificationHeaderLowPriority);
Selim Cinek414ad332017-02-24 19:06:12 -0800377 mNotificationHeaderLowPriority = null;
378 mNotificationHeaderWrapperLowPriority = null;
379 }
380 }
381
Mady Mellorb0a82462016-04-30 17:31:02 -0700382 public void updateChildrenHeaderAppearance() {
383 mHeaderUtil.updateChildrenHeaderAppearance();
Selim Cinekc897bd32016-03-18 17:32:31 -0700384 }
385
386 public void updateGroupOverflow() {
387 int childCount = mChildren.size();
388 int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
389 if (childCount > maxAllowedVisibleChildren) {
390 mOverflowNumber = mHybridGroupManager.bindOverflowNumber(
391 mOverflowNumber, childCount - maxAllowedVisibleChildren);
Selim Cinekc897bd32016-03-18 17:32:31 -0700392 if (mGroupOverFlowState == null) {
393 mGroupOverFlowState = new ViewState();
394 mNeverAppliedGroupState = true;
395 }
396 } else if (mOverflowNumber != null) {
397 removeView(mOverflowNumber);
Eliot Courtneybb8af1c2018-01-11 16:44:45 +0900398 if (isShown() && isAttachedToWindow()) {
Selim Cinekc897bd32016-03-18 17:32:31 -0700399 final View removedOverflowNumber = mOverflowNumber;
400 addTransientView(removedOverflowNumber, getTransientViewCount());
401 CrossFadeHelper.fadeOut(removedOverflowNumber, new Runnable() {
402 @Override
403 public void run() {
404 removeTransientView(removedOverflowNumber);
405 }
406 });
407 }
408 mOverflowNumber = null;
Selim Cinekc897bd32016-03-18 17:32:31 -0700409 mGroupOverFlowState = null;
410 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100411 }
412
Selim Cineka3d3b912016-02-02 11:22:06 -0800413 @Override
414 protected void onConfigurationChanged(Configuration newConfig) {
415 super.onConfigurationChanged(newConfig);
Selim Cinekc897bd32016-03-18 17:32:31 -0700416 updateGroupOverflow();
Selim Cineka3d3b912016-02-02 11:22:06 -0800417 }
418
Selim Cinekb5605e52015-02-20 18:21:41 +0100419 private View inflateDivider() {
420 return LayoutInflater.from(mContext).inflate(
421 R.layout.notification_children_divider, this, false);
422 }
423
424 public List<ExpandableNotificationRow> getNotificationChildren() {
425 return mChildren;
426 }
427
428 /**
429 * Apply the order given in the list to the children.
430 *
431 * @param childOrder the new list order
Selim Cineka7d4f822016-12-06 14:34:47 -0800432 * @param visualStabilityManager
433 * @param callback
Selim Cinekb5605e52015-02-20 18:21:41 +0100434 * @return whether the list order has changed
435 */
Selim Cineka7d4f822016-12-06 14:34:47 -0800436 public boolean applyChildOrder(List<ExpandableNotificationRow> childOrder,
437 VisualStabilityManager visualStabilityManager,
438 VisualStabilityManager.Callback callback) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100439 if (childOrder == null) {
440 return false;
441 }
442 boolean result = false;
443 for (int i = 0; i < mChildren.size() && i < childOrder.size(); i++) {
444 ExpandableNotificationRow child = mChildren.get(i);
445 ExpandableNotificationRow desiredChild = childOrder.get(i);
446 if (child != desiredChild) {
Selim Cineka7d4f822016-12-06 14:34:47 -0800447 if (visualStabilityManager.canReorderNotification(desiredChild)) {
448 mChildren.remove(desiredChild);
449 mChildren.add(i, desiredChild);
450 result = true;
451 } else {
452 visualStabilityManager.addReorderingAllowedCallback(callback);
453 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100454 }
455 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700456 updateExpansionStates();
Selim Cinekb5605e52015-02-20 18:21:41 +0100457 return result;
458 }
459
Selim Cinek83bc7832015-10-22 13:26:54 -0700460 private void updateExpansionStates() {
Selim Cinek898d1732016-02-29 19:57:35 -0800461 if (mChildrenExpanded || mUserLocked) {
462 // we don't modify it the group is expanded or if we are expanding it
463 return;
464 }
465 int size = mChildren.size();
466 for (int i = 0; i < size; i++) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700467 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinek898d1732016-02-29 19:57:35 -0800468 child.setSystemChildExpanded(i == 0 && size == 1);
Selim Cinek83bc7832015-10-22 13:26:54 -0700469 }
470 }
471
472 /**
473 *
474 * @return the intrinsic size of this children container, i.e the natural fully expanded state
475 */
Selim Cinekb5605e52015-02-20 18:21:41 +0100476 public int getIntrinsicHeight() {
Selim Cinek83bc7832015-10-22 13:26:54 -0700477 int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren();
478 return getIntrinsicHeight(maxAllowedVisibleChildren);
479 }
480
481 /**
482 * @return the intrinsic height with a number of children given
483 * in @param maxAllowedVisibleChildren
484 */
485 private int getIntrinsicHeight(float maxAllowedVisibleChildren) {
Selim Cinek414ad332017-02-24 19:06:12 -0800486 if (showingAsLowPriority()) {
487 return mNotificationHeaderLowPriority.getHeight();
488 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700489 int intrinsicHeight = mNotificationHeaderMargin;
Selim Cinekb5605e52015-02-20 18:21:41 +0100490 int visibleChildren = 0;
Selim Cinek83bc7832015-10-22 13:26:54 -0700491 int childCount = mChildren.size();
Selim Cinek42357e02016-02-24 18:48:01 -0800492 boolean firstChild = true;
493 float expandFactor = 0;
494 if (mUserLocked) {
Selim Cinekf07d0622016-03-21 19:52:52 -0700495 expandFactor = getGroupExpandFraction();
Selim Cinek42357e02016-02-24 18:48:01 -0800496 }
Adrian Roos6f6e1592017-05-02 16:22:53 -0700497 boolean childrenExpanded = mChildrenExpanded || mContainingNotification.isShowingAmbient();
Selim Cinekb5605e52015-02-20 18:21:41 +0100498 for (int i = 0; i < childCount; i++) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700499 if (visibleChildren >= maxAllowedVisibleChildren) {
500 break;
501 }
Selim Cinek42357e02016-02-24 18:48:01 -0800502 if (!firstChild) {
503 if (mUserLocked) {
504 intrinsicHeight += NotificationUtils.interpolate(mChildPadding, mDividerHeight,
505 expandFactor);
506 } else {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700507 intrinsicHeight += childrenExpanded ? mDividerHeight : mChildPadding;
Selim Cinek42357e02016-02-24 18:48:01 -0800508 }
509 } else {
510 if (mUserLocked) {
511 intrinsicHeight += NotificationUtils.interpolate(
512 0,
513 mNotificatonTopPadding + mDividerHeight,
514 expandFactor);
515 } else {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700516 intrinsicHeight += childrenExpanded
Selim Cinek42357e02016-02-24 18:48:01 -0800517 ? mNotificatonTopPadding + mDividerHeight
518 : 0;
519 }
520 firstChild = false;
521 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100522 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinekb5605e52015-02-20 18:21:41 +0100523 intrinsicHeight += child.getIntrinsicHeight();
524 visibleChildren++;
525 }
Selim Cinek42357e02016-02-24 18:48:01 -0800526 if (mUserLocked) {
527 intrinsicHeight += NotificationUtils.interpolate(mCollapsedBottompadding, 0.0f,
528 expandFactor);
Adrian Roos6f6e1592017-05-02 16:22:53 -0700529 } else if (!childrenExpanded) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700530 intrinsicHeight += mCollapsedBottompadding;
531 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100532 return intrinsicHeight;
533 }
534
535 /**
536 * Update the state of all its children based on a linear layout algorithm.
Selim Cinekc25989e2018-02-16 16:42:14 -0800537 * @param resultState the state to update
Selim Cinekb5605e52015-02-20 18:21:41 +0100538 * @param parentState the state of the parent
Selim Cinekc25989e2018-02-16 16:42:14 -0800539 * @param ambientState
Selim Cinekb5605e52015-02-20 18:21:41 +0100540 */
Selim Cinekc25989e2018-02-16 16:42:14 -0800541 public void getState(StackScrollState resultState, ExpandableViewState parentState,
542 AmbientState ambientState) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100543 int childCount = mChildren.size();
Mady Mellorb0a82462016-04-30 17:31:02 -0700544 int yPosition = mNotificationHeaderMargin;
Selim Cinekb5605e52015-02-20 18:21:41 +0100545 boolean firstChild = true;
Selim Cinek83bc7832015-10-22 13:26:54 -0700546 int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren();
Selim Cinek91c2a152016-03-17 15:01:38 -0700547 int lastVisibleIndex = maxAllowedVisibleChildren - 1;
Selim Cinekf07d0622016-03-21 19:52:52 -0700548 int firstOverflowIndex = lastVisibleIndex + 1;
Selim Cinek42357e02016-02-24 18:48:01 -0800549 float expandFactor = 0;
Selim Cinek414ad332017-02-24 19:06:12 -0800550 boolean expandingToExpandedGroup = mUserLocked && !showingAsLowPriority();
Selim Cinek42357e02016-02-24 18:48:01 -0800551 if (mUserLocked) {
Selim Cinekf07d0622016-03-21 19:52:52 -0700552 expandFactor = getGroupExpandFraction();
553 firstOverflowIndex = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
Selim Cinek42357e02016-02-24 18:48:01 -0800554 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700555
Adrian Roos16fe4952017-05-18 16:10:21 -0700556 boolean childrenExpandedAndNotAnimating = mChildrenExpanded
557 && !mContainingNotification.isGroupExpansionChanging();
Selim Cinekc25989e2018-02-16 16:42:14 -0800558 int launchTransitionCompensation = 0;
Selim Cinekb5605e52015-02-20 18:21:41 +0100559 for (int i = 0; i < childCount; i++) {
560 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinekb5605e52015-02-20 18:21:41 +0100561 if (!firstChild) {
Selim Cinek414ad332017-02-24 19:06:12 -0800562 if (expandingToExpandedGroup) {
Mady Mellorb0a82462016-04-30 17:31:02 -0700563 yPosition += NotificationUtils.interpolate(mChildPadding, mDividerHeight,
564 expandFactor);
Selim Cinek42357e02016-02-24 18:48:01 -0800565 } else {
Adrian Roos16fe4952017-05-18 16:10:21 -0700566 yPosition += mChildrenExpanded ? mDividerHeight : mChildPadding;
Selim Cinek42357e02016-02-24 18:48:01 -0800567 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100568 } else {
Selim Cinek414ad332017-02-24 19:06:12 -0800569 if (expandingToExpandedGroup) {
Selim Cinek42357e02016-02-24 18:48:01 -0800570 yPosition += NotificationUtils.interpolate(
571 0,
572 mNotificatonTopPadding + mDividerHeight,
573 expandFactor);
574 } else {
Adrian Roos16fe4952017-05-18 16:10:21 -0700575 yPosition += mChildrenExpanded ? mNotificatonTopPadding + mDividerHeight : 0;
Selim Cinek42357e02016-02-24 18:48:01 -0800576 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100577 firstChild = false;
578 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700579
Selim Cinekbbcebde2016-11-09 18:28:20 -0800580 ExpandableViewState childState = resultState.getViewStateForView(child);
Selim Cinekb5605e52015-02-20 18:21:41 +0100581 int intrinsicHeight = child.getIntrinsicHeight();
Selim Cinekb3dadcc2016-11-21 17:21:13 -0800582 childState.height = intrinsicHeight;
Selim Cinekc25989e2018-02-16 16:42:14 -0800583 childState.yTranslation = yPosition + launchTransitionCompensation;
Selim Cinekb3dadcc2016-11-21 17:21:13 -0800584 childState.hidden = false;
Mady Mellorb0a82462016-04-30 17:31:02 -0700585 // When the group is expanded, the children cast the shadows rather than the parent
586 // so use the parent's elevation here.
Adrian Roos16fe4952017-05-18 16:10:21 -0700587 childState.zTranslation =
588 (childrenExpandedAndNotAnimating && mEnableShadowOnChildNotifications)
Selim Cinekc25989e2018-02-16 16:42:14 -0800589 ? parentState.zTranslation
Mady Mellorb0a82462016-04-30 17:31:02 -0700590 : 0;
Selim Cinekb5605e52015-02-20 18:21:41 +0100591 childState.dimmed = parentState.dimmed;
592 childState.dark = parentState.dark;
593 childState.hideSensitive = parentState.hideSensitive;
Selim Cinekdb167372016-11-17 15:41:17 -0800594 childState.belowSpeedBump = parentState.belowSpeedBump;
Selim Cinekb5605e52015-02-20 18:21:41 +0100595 childState.clipTopAmount = 0;
Selim Cinekf07d0622016-03-21 19:52:52 -0700596 childState.alpha = 0;
597 if (i < firstOverflowIndex) {
Selim Cinek414ad332017-02-24 19:06:12 -0800598 childState.alpha = showingAsLowPriority() ? expandFactor : 1.0f;
Selim Cinekf07d0622016-03-21 19:52:52 -0700599 } else if (expandFactor == 1.0f && i <= lastVisibleIndex) {
600 childState.alpha = (mActualHeight - childState.yTranslation) / childState.height;
601 childState.alpha = Math.max(0.0f, Math.min(1.0f, childState.alpha));
602 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100603 childState.location = parentState.location;
Selim Cineka7d4f822016-12-06 14:34:47 -0800604 childState.inShelf = parentState.inShelf;
Selim Cinekb5605e52015-02-20 18:21:41 +0100605 yPosition += intrinsicHeight;
Selim Cinekc25989e2018-02-16 16:42:14 -0800606 if (child.isExpandAnimationRunning()) {
607 launchTransitionCompensation = -ambientState.getExpandAnimationTopChange();
608 }
Selim Cineka7d4f822016-12-06 14:34:47 -0800609
Selim Cinekb5605e52015-02-20 18:21:41 +0100610 }
Selim Cinekc897bd32016-03-18 17:32:31 -0700611 if (mOverflowNumber != null) {
612 ExpandableNotificationRow overflowView = mChildren.get(Math.min(
613 getMaxAllowedVisibleChildren(true /* likeCollpased */), childCount) - 1);
614 mGroupOverFlowState.copyFrom(resultState.getViewStateForView(overflowView));
Adrian Roos6f6e1592017-05-02 16:22:53 -0700615
616 if (mContainingNotification.isShowingAmbient() || !mChildrenExpanded) {
617 HybridNotificationView alignView = null;
618 if (mContainingNotification.isShowingAmbient()) {
619 alignView = overflowView.getAmbientSingleLineView();
620 } else if (mUserLocked) {
621 alignView = overflowView.getSingleLineView();
622 }
623 if (alignView != null) {
624 View mirrorView = alignView.getTextView();
Selim Cinekc897bd32016-03-18 17:32:31 -0700625 if (mirrorView.getVisibility() == GONE) {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700626 mirrorView = alignView.getTitleView();
Selim Cinekc897bd32016-03-18 17:32:31 -0700627 }
628 if (mirrorView.getVisibility() == GONE) {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700629 mirrorView = alignView;
Selim Cinekc897bd32016-03-18 17:32:31 -0700630 }
631 mGroupOverFlowState.yTranslation += NotificationUtils.getRelativeYOffset(
632 mirrorView, overflowView);
633 mGroupOverFlowState.alpha = mirrorView.getAlpha();
634 }
635 } else {
Mady Mellorb0a82462016-04-30 17:31:02 -0700636 mGroupOverFlowState.yTranslation += mNotificationHeaderMargin;
Selim Cinekc897bd32016-03-18 17:32:31 -0700637 mGroupOverFlowState.alpha = 0.0f;
638 }
639 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700640 if (mNotificationHeader != null) {
641 if (mHeaderViewState == null) {
642 mHeaderViewState = new ViewState();
643 }
644 mHeaderViewState.initFrom(mNotificationHeader);
Adrian Roos16fe4952017-05-18 16:10:21 -0700645 mHeaderViewState.zTranslation = childrenExpandedAndNotAnimating
Selim Cinekc25989e2018-02-16 16:42:14 -0800646 ? parentState.zTranslation
Mady Mellorb0a82462016-04-30 17:31:02 -0700647 : 0;
648 }
649 }
650
651 /**
652 * When moving into the bottom stack, the bottom visible child in an expanded group adjusts its
653 * height, children in the group after this are gone.
654 *
655 * @param child the child who's height to adjust.
656 * @param parentHeight the height of the parent.
657 * @param childState the state to update.
658 * @param yPosition the yPosition of the view.
659 * @return true if children after this one should be hidden.
660 */
661 private boolean updateChildStateForExpandedGroup(ExpandableNotificationRow child,
Selim Cinekbbcebde2016-11-09 18:28:20 -0800662 int parentHeight, ExpandableViewState childState, int yPosition) {
Mady Mellorb0a82462016-04-30 17:31:02 -0700663 final int top = yPosition + child.getClipTopAmount();
664 final int intrinsicHeight = child.getIntrinsicHeight();
665 final int bottom = top + intrinsicHeight;
666 int newHeight = intrinsicHeight;
667 if (bottom >= parentHeight) {
668 // Child is either clipped or gone
669 newHeight = Math.max((parentHeight - top), 0);
670 }
671 childState.hidden = newHeight == 0;
672 childState.height = newHeight;
673 return childState.height != intrinsicHeight && !childState.hidden;
Selim Cinek83bc7832015-10-22 13:26:54 -0700674 }
675
676 private int getMaxAllowedVisibleChildren() {
677 return getMaxAllowedVisibleChildren(false /* likeCollapsed */);
678 }
679
680 private int getMaxAllowedVisibleChildren(boolean likeCollapsed) {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700681 if (mContainingNotification.isShowingAmbient()) {
682 return NUMBER_OF_CHILDREN_WHEN_AMBIENT;
683 }
Selim Cinek414ad332017-02-24 19:06:12 -0800684 if (!likeCollapsed && (mChildrenExpanded || mContainingNotification.isUserLocked())) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700685 return NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED;
686 }
Selim Cinek414ad332017-02-24 19:06:12 -0800687 if (mIsLowPriority || !mContainingNotification.isOnKeyguard()
688 && (mContainingNotification.isExpanded() || mContainingNotification.isHeadsUp())) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700689 return NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED;
690 }
691 return NUMBER_OF_CHILDREN_WHEN_COLLAPSED;
Selim Cinekb5605e52015-02-20 18:21:41 +0100692 }
693
694 public void applyState(StackScrollState state) {
695 int childCount = mChildren.size();
Selim Cinek83bc7832015-10-22 13:26:54 -0700696 ViewState tmpState = new ViewState();
Selim Cinekc897bd32016-03-18 17:32:31 -0700697 float expandFraction = 0.0f;
698 if (mUserLocked) {
Selim Cinekf07d0622016-03-21 19:52:52 -0700699 expandFraction = getGroupExpandFraction();
Selim Cinekc897bd32016-03-18 17:32:31 -0700700 }
Selim Cinek414ad332017-02-24 19:06:12 -0800701 final boolean dividersVisible = mUserLocked && !showingAsLowPriority()
Anthony Chen6bf88a02017-04-10 14:41:44 -0700702 || (mChildrenExpanded && mShowDividersWhenExpanded)
703 || (mContainingNotification.isGroupExpansionChanging()
704 && !mHideDividersDuringExpand);
Selim Cinekb5605e52015-02-20 18:21:41 +0100705 for (int i = 0; i < childCount; i++) {
706 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinekbbcebde2016-11-09 18:28:20 -0800707 ExpandableViewState viewState = state.getViewStateForView(child);
708 viewState.applyToView(child);
Selim Cinek7b836392015-12-04 20:02:59 -0800709
710 // layout the divider
711 View divider = mDividers.get(i);
712 tmpState.initFrom(divider);
713 tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
Anthony Chen6bf88a02017-04-10 14:41:44 -0700714 float alpha = mChildrenExpanded && viewState.alpha != 0 ? mDividerAlpha : 0;
Selim Cinek414ad332017-02-24 19:06:12 -0800715 if (mUserLocked && !showingAsLowPriority() && viewState.alpha != 0) {
Selim Cinekf07d0622016-03-21 19:52:52 -0700716 alpha = NotificationUtils.interpolate(0, 0.5f,
717 Math.min(viewState.alpha, expandFraction));
Selim Cinek42357e02016-02-24 18:48:01 -0800718 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700719 tmpState.hidden = !dividersVisible;
Selim Cinek42357e02016-02-24 18:48:01 -0800720 tmpState.alpha = alpha;
Selim Cinekbbcebde2016-11-09 18:28:20 -0800721 tmpState.applyToView(divider);
Selim Cinekd9a3f8f2016-03-17 19:39:00 -0700722 // There is no fake shadow to be drawn on the children
723 child.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
Selim Cinekb5605e52015-02-20 18:21:41 +0100724 }
Selim Cinek0cfbef42016-11-09 19:06:36 -0800725 if (mGroupOverFlowState != null) {
Selim Cinekbbcebde2016-11-09 18:28:20 -0800726 mGroupOverFlowState.applyToView(mOverflowNumber);
Selim Cinekc897bd32016-03-18 17:32:31 -0700727 mNeverAppliedGroupState = false;
728 }
Selim Cinek0cfbef42016-11-09 19:06:36 -0800729 if (mHeaderViewState != null) {
Selim Cinekbbcebde2016-11-09 18:28:20 -0800730 mHeaderViewState.applyToView(mNotificationHeader);
Mady Mellorb0a82462016-04-30 17:31:02 -0700731 }
Selim Cinekb3dadcc2016-11-21 17:21:13 -0800732 updateChildrenClipping();
733 }
734
735 private void updateChildrenClipping() {
Selim Cinekc25989e2018-02-16 16:42:14 -0800736 if (mContainingNotification.hasExpandingChild()) {
737 return;
738 }
Selim Cinekb3dadcc2016-11-21 17:21:13 -0800739 int childCount = mChildren.size();
Selim Cinek414ad332017-02-24 19:06:12 -0800740 int layoutEnd = mContainingNotification.getActualHeight() - mClipBottomAmount;
Selim Cinekb3dadcc2016-11-21 17:21:13 -0800741 for (int i = 0; i < childCount; i++) {
742 ExpandableNotificationRow child = mChildren.get(i);
743 if (child.getVisibility() == GONE) {
744 continue;
745 }
746 float childTop = child.getTranslationY();
747 float childBottom = childTop + child.getActualHeight();
748 boolean visible = true;
749 int clipBottomAmount = 0;
750 if (childTop > layoutEnd) {
751 visible = false;
752 } else if (childBottom > layoutEnd) {
753 clipBottomAmount = (int) (childBottom - layoutEnd);
754 }
755
756 boolean isVisible = child.getVisibility() == VISIBLE;
757 if (visible != isVisible) {
758 child.setVisibility(visible ? VISIBLE : INVISIBLE);
759 }
760
761 child.setClipBottomAmount(clipBottomAmount);
762 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100763 }
764
Selim Cinekb5605e52015-02-20 18:21:41 +0100765 /**
766 * This is called when the children expansion has changed and positions the children properly
767 * for an appear animation.
768 *
769 * @param state the new state we animate to
770 */
771 public void prepareExpansionChanged(StackScrollState state) {
Selim Cinek277a8aa2016-01-22 12:12:37 -0800772 // TODO: do something that makes sense, like placing the invisible views correctly
773 return;
Selim Cinekb5605e52015-02-20 18:21:41 +0100774 }
775
Selim Cinek0cfbef42016-11-09 19:06:36 -0800776 public void startAnimationToState(StackScrollState state, AnimationProperties properties) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100777 int childCount = mChildren.size();
Selim Cinek83bc7832015-10-22 13:26:54 -0700778 ViewState tmpState = new ViewState();
Selim Cinekf07d0622016-03-21 19:52:52 -0700779 float expandFraction = getGroupExpandFraction();
Selim Cinek414ad332017-02-24 19:06:12 -0800780 final boolean dividersVisible = mUserLocked && !showingAsLowPriority()
Anthony Chen6bf88a02017-04-10 14:41:44 -0700781 || (mChildrenExpanded && mShowDividersWhenExpanded)
782 || (mContainingNotification.isGroupExpansionChanging()
783 && !mHideDividersDuringExpand);
Selim Cineke8126522015-12-08 21:21:17 -0800784 for (int i = childCount - 1; i >= 0; i--) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100785 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinekbbcebde2016-11-09 18:28:20 -0800786 ExpandableViewState viewState = state.getViewStateForView(child);
Selim Cinek0cfbef42016-11-09 19:06:36 -0800787 viewState.animateTo(child, properties);
Selim Cinek7b836392015-12-04 20:02:59 -0800788
789 // layout the divider
790 View divider = mDividers.get(i);
791 tmpState.initFrom(divider);
792 tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
Selim Cinek42357e02016-02-24 18:48:01 -0800793 float alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
Selim Cinek414ad332017-02-24 19:06:12 -0800794 if (mUserLocked && !showingAsLowPriority() && viewState.alpha != 0) {
Selim Cinekf07d0622016-03-21 19:52:52 -0700795 alpha = NotificationUtils.interpolate(0, 0.5f,
796 Math.min(viewState.alpha, expandFraction));
Selim Cinek42357e02016-02-24 18:48:01 -0800797 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700798 tmpState.hidden = !dividersVisible;
Selim Cinek42357e02016-02-24 18:48:01 -0800799 tmpState.alpha = alpha;
Selim Cinek0cfbef42016-11-09 19:06:36 -0800800 tmpState.animateTo(divider, properties);
Selim Cinekd9a3f8f2016-03-17 19:39:00 -0700801 // There is no fake shadow to be drawn on the children
802 child.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
Selim Cinekb5605e52015-02-20 18:21:41 +0100803 }
Selim Cinekc897bd32016-03-18 17:32:31 -0700804 if (mOverflowNumber != null) {
805 if (mNeverAppliedGroupState) {
806 float alpha = mGroupOverFlowState.alpha;
807 mGroupOverFlowState.alpha = 0;
Selim Cinekbbcebde2016-11-09 18:28:20 -0800808 mGroupOverFlowState.applyToView(mOverflowNumber);
Selim Cinekc897bd32016-03-18 17:32:31 -0700809 mGroupOverFlowState.alpha = alpha;
810 mNeverAppliedGroupState = false;
811 }
Selim Cinek0cfbef42016-11-09 19:06:36 -0800812 mGroupOverFlowState.animateTo(mOverflowNumber, properties);
Selim Cinekc897bd32016-03-18 17:32:31 -0700813 }
Mady Mellorb0a82462016-04-30 17:31:02 -0700814 if (mNotificationHeader != null) {
Selim Cinekbbcebde2016-11-09 18:28:20 -0800815 mHeaderViewState.applyToView(mNotificationHeader);
Mady Mellorb0a82462016-04-30 17:31:02 -0700816 }
Selim Cinekb3dadcc2016-11-21 17:21:13 -0800817 updateChildrenClipping();
Selim Cinekb5605e52015-02-20 18:21:41 +0100818 }
819
820 public ExpandableNotificationRow getViewAtPosition(float y) {
821 // find the view under the pointer, accounting for GONE views
822 final int count = mChildren.size();
823 for (int childIdx = 0; childIdx < count; childIdx++) {
824 ExpandableNotificationRow slidingChild = mChildren.get(childIdx);
825 float childTop = slidingChild.getTranslationY();
826 float top = childTop + slidingChild.getClipTopAmount();
827 float bottom = childTop + slidingChild.getActualHeight();
828 if (y >= top && y <= bottom) {
829 return slidingChild;
830 }
831 }
832 return null;
833 }
834
Selim Cinek83bc7832015-10-22 13:26:54 -0700835 public void setChildrenExpanded(boolean childrenExpanded) {
836 mChildrenExpanded = childrenExpanded;
Selim Cinek898d1732016-02-29 19:57:35 -0800837 updateExpansionStates();
Mady Mellorb0a82462016-04-30 17:31:02 -0700838 if (mNotificationHeader != null) {
839 mNotificationHeader.setExpanded(childrenExpanded);
840 }
Selim Cinekddf1b392016-05-27 16:33:10 -0700841 final int count = mChildren.size();
842 for (int childIdx = 0; childIdx < count; childIdx++) {
843 ExpandableNotificationRow child = mChildren.get(childIdx);
844 child.setChildrenExpanded(childrenExpanded, false);
845 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700846 }
847
Selim Cinek414ad332017-02-24 19:06:12 -0800848 public void setContainingNotification(ExpandableNotificationRow parent) {
849 mContainingNotification = parent;
850 mHeaderUtil = new NotificationHeaderUtil(mContainingNotification);
Mady Mellorb0a82462016-04-30 17:31:02 -0700851 }
852
Selim Cinek414ad332017-02-24 19:06:12 -0800853 public ExpandableNotificationRow getContainingNotification() {
854 return mContainingNotification;
Adrian Roos4a579672016-05-24 16:54:37 -0700855 }
856
Mady Mellorb0a82462016-04-30 17:31:02 -0700857 public NotificationHeaderView getHeaderView() {
858 return mNotificationHeader;
859 }
860
Selim Cinek414ad332017-02-24 19:06:12 -0800861 public NotificationHeaderView getLowPriorityHeaderView() {
862 return mNotificationHeaderLowPriority;
863 }
864
Lucas Dupin6ea27872017-05-30 12:00:04 -0700865 @VisibleForTesting
866 public ViewGroup getCurrentHeaderView() {
867 return mCurrentHeader;
868 }
869
Adrian Roos6f6e1592017-05-02 16:22:53 -0700870 public void notifyShowAmbientChanged() {
871 updateHeaderVisibility(false);
872 }
873
Selim Cinek414ad332017-02-24 19:06:12 -0800874 private void updateHeaderVisibility(boolean animate) {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700875 ViewGroup desiredHeader;
876 ViewGroup currentHeader = mCurrentHeader;
877 desiredHeader = calculateDesiredHeader();
878
879 if (currentHeader == desiredHeader) {
880 return;
Mady Mellorb0a82462016-04-30 17:31:02 -0700881 }
Adrian Roos6f6e1592017-05-02 16:22:53 -0700882 if (desiredHeader == mNotificationHeaderAmbient
883 || currentHeader == mNotificationHeaderAmbient) {
884 animate = false;
885 }
886
Selim Cinek414ad332017-02-24 19:06:12 -0800887 if (animate) {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700888 if (desiredHeader != null && currentHeader != null) {
889 currentHeader.setVisibility(VISIBLE);
890 desiredHeader.setVisibility(VISIBLE);
891 NotificationViewWrapper visibleWrapper = getWrapperForView(desiredHeader);
892 NotificationViewWrapper hiddenWrapper = getWrapperForView(currentHeader);
Selim Cinek414ad332017-02-24 19:06:12 -0800893 visibleWrapper.transformFrom(hiddenWrapper);
894 hiddenWrapper.transformTo(visibleWrapper, () -> updateHeaderVisibility(false));
Adrian Roos6f6e1592017-05-02 16:22:53 -0700895 startChildAlphaAnimations(desiredHeader == mNotificationHeader);
Selim Cinek414ad332017-02-24 19:06:12 -0800896 } else {
897 animate = false;
898 }
899 }
900 if (!animate) {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700901 if (desiredHeader != null) {
902 getWrapperForView(desiredHeader).setVisible(true);
903 desiredHeader.setVisibility(VISIBLE);
Selim Cinek414ad332017-02-24 19:06:12 -0800904 }
Adrian Roos6f6e1592017-05-02 16:22:53 -0700905 if (currentHeader != null) {
Lucas Dupin6ea27872017-05-30 12:00:04 -0700906 // Wrapper can be null if we were a low priority notification
907 // and just destroyed it by calling setIsLowPriority(false)
908 NotificationViewWrapper wrapper = getWrapperForView(currentHeader);
909 if (wrapper != null) {
910 wrapper.setVisible(false);
911 }
Adrian Roos6f6e1592017-05-02 16:22:53 -0700912 currentHeader.setVisibility(INVISIBLE);
Selim Cinek414ad332017-02-24 19:06:12 -0800913 }
914 }
Adrian Roos6f6e1592017-05-02 16:22:53 -0700915
916 resetHeaderVisibilityIfNeeded(mNotificationHeader, desiredHeader);
917 resetHeaderVisibilityIfNeeded(mNotificationHeaderAmbient, desiredHeader);
918 resetHeaderVisibilityIfNeeded(mNotificationHeaderLowPriority, desiredHeader);
919
Lucas Dupin6ea27872017-05-30 12:00:04 -0700920 mCurrentHeader = desiredHeader;
Adrian Roos6f6e1592017-05-02 16:22:53 -0700921 }
922
923 private void resetHeaderVisibilityIfNeeded(View header, View desiredHeader) {
924 if (header == null) {
925 return;
926 }
927 if (header != mCurrentHeader && header != desiredHeader) {
928 getWrapperForView(header).setVisible(false);
929 header.setVisibility(INVISIBLE);
930 }
931 if (header == desiredHeader && header.getVisibility() != VISIBLE) {
932 getWrapperForView(header).setVisible(true);
933 header.setVisibility(VISIBLE);
934 }
935 }
936
937 private ViewGroup calculateDesiredHeader() {
938 ViewGroup desiredHeader;
939 if (mContainingNotification.isShowingAmbient()) {
940 desiredHeader = mNotificationHeaderAmbient;
941 } else if (showingAsLowPriority()) {
942 desiredHeader = mNotificationHeaderLowPriority;
943 } else {
944 desiredHeader = mNotificationHeader;
945 }
946 return desiredHeader;
Selim Cinek414ad332017-02-24 19:06:12 -0800947 }
948
949 private void startChildAlphaAnimations(boolean toVisible) {
950 float target = toVisible ? 1.0f : 0.0f;
951 float start = 1.0f - target;
952 int childCount = mChildren.size();
953 for (int i = 0; i < childCount; i++) {
954 if (i >= NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED) {
955 break;
956 }
957 ExpandableNotificationRow child = mChildren.get(i);
958 child.setAlpha(start);
959 ViewState viewState = new ViewState();
960 viewState.initFrom(child);
961 viewState.alpha = target;
962 ALPHA_FADE_IN.setDelay(i * 50);
963 viewState.animateTo(child, ALPHA_FADE_IN);
964 }
965 }
966
967
968 private void updateHeaderTransformation() {
Selim Cinek1826d982017-03-06 20:47:37 -0800969 if (mUserLocked && showingAsLowPriority()) {
Selim Cinek414ad332017-02-24 19:06:12 -0800970 float fraction = getGroupExpandFraction();
971 mNotificationHeaderWrapper.transformFrom(mNotificationHeaderWrapperLowPriority,
972 fraction);
973 mNotificationHeader.setVisibility(VISIBLE);
974 mNotificationHeaderWrapperLowPriority.transformTo(mNotificationHeaderWrapper,
975 fraction);
976 }
977
978 }
979
Adrian Roos6f6e1592017-05-02 16:22:53 -0700980 private NotificationViewWrapper getWrapperForView(View visibleHeader) {
Selim Cinek414ad332017-02-24 19:06:12 -0800981 if (visibleHeader == mNotificationHeader) {
982 return mNotificationHeaderWrapper;
983 }
Adrian Roos6f6e1592017-05-02 16:22:53 -0700984 if (visibleHeader == mNotificationHeaderAmbient) {
985 return mNotificationHeaderWrapperAmbient;
986 }
Selim Cinek414ad332017-02-24 19:06:12 -0800987 return mNotificationHeaderWrapperLowPriority;
Mady Mellorb0a82462016-04-30 17:31:02 -0700988 }
989
990 /**
991 * Called when a groups expansion changes to adjust the background of the header view.
992 *
993 * @param expanded whether the group is expanded.
994 */
995 public void updateHeaderForExpansion(boolean expanded) {
996 if (mNotificationHeader != null) {
997 if (expanded) {
998 ColorDrawable cd = new ColorDrawable();
Selim Cinek414ad332017-02-24 19:06:12 -0800999 cd.setColor(mContainingNotification.calculateBgColor());
Mady Mellorb0a82462016-04-30 17:31:02 -07001000 mNotificationHeader.setHeaderBackgroundDrawable(cd);
1001 } else {
1002 mNotificationHeader.setHeaderBackgroundDrawable(null);
1003 }
1004 }
Selim Cinek388df6d2015-10-22 13:25:11 -07001005 }
1006
Selim Cinek83bc7832015-10-22 13:26:54 -07001007 public int getMaxContentHeight() {
Selim Cinek414ad332017-02-24 19:06:12 -08001008 if (showingAsLowPriority()) {
1009 return getMinHeight(NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED, true
1010 /* likeHighPriority */);
1011 }
Mady Mellorb0a82462016-04-30 17:31:02 -07001012 int maxContentHeight = mNotificationHeaderMargin + mNotificatonTopPadding;
Selim Cinek42357e02016-02-24 18:48:01 -08001013 int visibleChildren = 0;
1014 int childCount = mChildren.size();
1015 for (int i = 0; i < childCount; i++) {
1016 if (visibleChildren >= NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED) {
1017 break;
1018 }
1019 ExpandableNotificationRow child = mChildren.get(i);
Selim Cineke81b82b2016-03-04 11:22:28 -08001020 float childHeight = child.isExpanded(true /* allowOnKeyguard */)
Selim Cinek42357e02016-02-24 18:48:01 -08001021 ? child.getMaxExpandHeight()
1022 : child.getShowingLayout().getMinHeight(true /* likeGroupExpanded */);
1023 maxContentHeight += childHeight;
1024 visibleChildren++;
1025 }
1026 if (visibleChildren > 0) {
1027 maxContentHeight += visibleChildren * mDividerHeight;
1028 }
1029 return maxContentHeight;
1030 }
1031
1032 public void setActualHeight(int actualHeight) {
1033 if (!mUserLocked) {
1034 return;
1035 }
1036 mActualHeight = actualHeight;
Selim Cinekf07d0622016-03-21 19:52:52 -07001037 float fraction = getGroupExpandFraction();
Selim Cinek414ad332017-02-24 19:06:12 -08001038 boolean showingLowPriority = showingAsLowPriority();
1039 updateHeaderTransformation();
Selim Cinekf07d0622016-03-21 19:52:52 -07001040 int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* forceCollapsed */);
Selim Cinek42357e02016-02-24 18:48:01 -08001041 int childCount = mChildren.size();
1042 for (int i = 0; i < childCount; i++) {
1043 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinek414ad332017-02-24 19:06:12 -08001044 float childHeight;
1045 if (showingLowPriority) {
1046 childHeight = child.getShowingLayout().getMinHeight(false /* likeGroupExpanded */);
1047 } else if (child.isExpanded(true /* allowOnKeyguard */)) {
1048 childHeight = child.getMaxExpandHeight();
1049 } else {
1050 childHeight = child.getShowingLayout().getMinHeight(
1051 true /* likeGroupExpanded */);
1052 }
Selim Cinekf07d0622016-03-21 19:52:52 -07001053 if (i < maxAllowedVisibleChildren) {
1054 float singleLineHeight = child.getShowingLayout().getMinHeight(
1055 false /* likeGroupExpanded */);
1056 child.setActualHeight((int) NotificationUtils.interpolate(singleLineHeight,
1057 childHeight, fraction), false);
1058 } else {
1059 child.setActualHeight((int) childHeight, false);
1060 }
Selim Cinek42357e02016-02-24 18:48:01 -08001061 }
1062 }
1063
Selim Cinekf07d0622016-03-21 19:52:52 -07001064 public float getGroupExpandFraction() {
Selim Cinek414ad332017-02-24 19:06:12 -08001065 int visibleChildrenExpandedHeight = showingAsLowPriority() ? getMaxContentHeight()
1066 : getVisibleChildrenExpandHeight();
Selim Cinek567e8452016-03-24 10:54:56 -07001067 int minExpandHeight = getCollapsedHeight();
Selim Cinekf07d0622016-03-21 19:52:52 -07001068 float factor = (mActualHeight - minExpandHeight)
1069 / (float) (visibleChildrenExpandedHeight - minExpandHeight);
Selim Cinek42357e02016-02-24 18:48:01 -08001070 return Math.max(0.0f, Math.min(1.0f, factor));
1071 }
1072
Selim Cinekf07d0622016-03-21 19:52:52 -07001073 private int getVisibleChildrenExpandHeight() {
Mady Mellorb0a82462016-04-30 17:31:02 -07001074 int intrinsicHeight = mNotificationHeaderMargin + mNotificatonTopPadding + mDividerHeight;
Selim Cinek42357e02016-02-24 18:48:01 -08001075 int visibleChildren = 0;
1076 int childCount = mChildren.size();
Selim Cinekf07d0622016-03-21 19:52:52 -07001077 int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* forceCollapsed */);
Selim Cinek42357e02016-02-24 18:48:01 -08001078 for (int i = 0; i < childCount; i++) {
Selim Cinekf07d0622016-03-21 19:52:52 -07001079 if (visibleChildren >= maxAllowedVisibleChildren) {
Selim Cinek42357e02016-02-24 18:48:01 -08001080 break;
1081 }
1082 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinekf07d0622016-03-21 19:52:52 -07001083 float childHeight = child.isExpanded(true /* allowOnKeyguard */)
1084 ? child.getMaxExpandHeight()
1085 : child.getShowingLayout().getMinHeight(true /* likeGroupExpanded */);
1086 intrinsicHeight += childHeight;
Selim Cinek42357e02016-02-24 18:48:01 -08001087 visibleChildren++;
1088 }
Selim Cinek42357e02016-02-24 18:48:01 -08001089 return intrinsicHeight;
Selim Cinek83bc7832015-10-22 13:26:54 -07001090 }
1091
1092 public int getMinHeight() {
Adrian Roos6f6e1592017-05-02 16:22:53 -07001093 return getMinHeight(mContainingNotification.isShowingAmbient()
1094 ? NUMBER_OF_CHILDREN_WHEN_AMBIENT
1095 : NUMBER_OF_CHILDREN_WHEN_COLLAPSED, false /* likeHighPriority */);
Selim Cinekb55386d2015-12-16 17:26:49 -08001096 }
1097
Selim Cinek567e8452016-03-24 10:54:56 -07001098 public int getCollapsedHeight() {
Selim Cinek414ad332017-02-24 19:06:12 -08001099 return getMinHeight(getMaxAllowedVisibleChildren(true /* forceCollapsed */),
1100 false /* likeHighPriority */);
Selim Cinek567e8452016-03-24 10:54:56 -07001101 }
1102
Selim Cinek414ad332017-02-24 19:06:12 -08001103 /**
1104 * Get the minimum Height for this group.
1105 *
1106 * @param maxAllowedVisibleChildren the number of children that should be visible
1107 * @param likeHighPriority if the height should be calculated as if it were not low priority
1108 */
1109 private int getMinHeight(int maxAllowedVisibleChildren, boolean likeHighPriority) {
1110 if (!likeHighPriority && showingAsLowPriority()) {
1111 return mNotificationHeaderLowPriority.getHeight();
Selim Cinek6743c0b2017-01-18 18:24:01 -08001112 }
Mady Mellorb0a82462016-04-30 17:31:02 -07001113 int minExpandHeight = mNotificationHeaderMargin;
Selim Cinek2c584612016-02-29 16:14:25 -08001114 int visibleChildren = 0;
1115 boolean firstChild = true;
1116 int childCount = mChildren.size();
1117 for (int i = 0; i < childCount; i++) {
1118 if (visibleChildren >= maxAllowedVisibleChildren) {
1119 break;
1120 }
1121 if (!firstChild) {
1122 minExpandHeight += mChildPadding;
1123 } else {
1124 firstChild = false;
1125 }
1126 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinekf1f270a2016-04-05 19:02:37 -07001127 minExpandHeight += child.getSingleLineView().getHeight();
Selim Cinek2c584612016-02-29 16:14:25 -08001128 visibleChildren++;
1129 }
1130 minExpandHeight += mCollapsedBottompadding;
1131 return minExpandHeight;
Selim Cinekb5605e52015-02-20 18:21:41 +01001132 }
Selim Cinek9c7712d2015-12-08 19:19:48 -08001133
Selim Cinek414ad332017-02-24 19:06:12 -08001134 public boolean showingAsLowPriority() {
1135 return mIsLowPriority && !mContainingNotification.isExpanded();
1136 }
1137
Selim Cinekc897bd32016-03-18 17:32:31 -07001138 public void setDark(boolean dark, boolean fade, long delay) {
1139 if (mOverflowNumber != null) {
Adrian Roos6f6e1592017-05-02 16:22:53 -07001140 mHybridGroupManager.setOverflowNumberDark(mOverflowNumber, dark, fade, delay);
Selim Cinekc897bd32016-03-18 17:32:31 -07001141 }
Mady Mellorb0a82462016-04-30 17:31:02 -07001142 mNotificationHeaderWrapper.setDark(dark, fade, delay);
Selim Cinekc897bd32016-03-18 17:32:31 -07001143 }
1144
Mady Mellorb0a82462016-04-30 17:31:02 -07001145 public void reInflateViews(OnClickListener listener, StatusBarNotification notification) {
Selim Cinek414ad332017-02-24 19:06:12 -08001146 if (mNotificationHeader != null) {
1147 removeView(mNotificationHeader);
1148 mNotificationHeader = null;
1149 }
1150 if (mNotificationHeaderLowPriority != null) {
1151 removeView(mNotificationHeaderLowPriority);
1152 mNotificationHeaderLowPriority = null;
1153 }
Adrian Roos6f6e1592017-05-02 16:22:53 -07001154 if (mNotificationHeaderAmbient != null) {
1155 removeView(mNotificationHeaderAmbient);
1156 mNotificationHeaderAmbient = null;
1157 }
Selim Cinek414ad332017-02-24 19:06:12 -08001158 recreateNotificationHeader(listener);
Selim Cinek01af3342016-02-09 19:25:31 -08001159 initDimens();
1160 for (int i = 0; i < mDividers.size(); i++) {
1161 View prevDivider = mDividers.get(i);
1162 int index = indexOfChild(prevDivider);
1163 removeView(prevDivider);
1164 View divider = inflateDivider();
1165 addView(divider, index);
1166 mDividers.set(i, divider);
1167 }
Selim Cinek4bb59342016-04-08 19:29:35 -07001168 removeView(mOverflowNumber);
1169 mOverflowNumber = null;
Selim Cinek4bb59342016-04-08 19:29:35 -07001170 mGroupOverFlowState = null;
1171 updateGroupOverflow();
Selim Cinek01af3342016-02-09 19:25:31 -08001172 }
Selim Cinek42357e02016-02-24 18:48:01 -08001173
1174 public void setUserLocked(boolean userLocked) {
1175 mUserLocked = userLocked;
Selim Cinek414ad332017-02-24 19:06:12 -08001176 if (!mUserLocked) {
1177 updateHeaderVisibility(false /* animate */);
1178 }
Selim Cinek42357e02016-02-24 18:48:01 -08001179 int childCount = mChildren.size();
1180 for (int i = 0; i < childCount; i++) {
1181 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinek414ad332017-02-24 19:06:12 -08001182 child.setUserLocked(userLocked && !showingAsLowPriority());
Selim Cinek42357e02016-02-24 18:48:01 -08001183 }
1184 }
Selim Cinekc897bd32016-03-18 17:32:31 -07001185
1186 public void onNotificationUpdated() {
1187 mHybridGroupManager.setOverflowNumberColor(mOverflowNumber,
Adrian Roos6f6e1592017-05-02 16:22:53 -07001188 mContainingNotification.getNotificationColor(),
1189 mContainingNotification.getNotificationColorAmbient());
Selim Cinekc897bd32016-03-18 17:32:31 -07001190 }
Adrian Roosd009ab12016-05-20 17:58:53 -07001191
Adrian Roos4a579672016-05-24 16:54:37 -07001192 public int getPositionInLinearLayout(View childInGroup) {
1193 int position = mNotificationHeaderMargin + mNotificatonTopPadding;
1194
1195 for (int i = 0; i < mChildren.size(); i++) {
1196 ExpandableNotificationRow child = mChildren.get(i);
1197 boolean notGone = child.getVisibility() != View.GONE;
1198 if (notGone) {
1199 position += mDividerHeight;
1200 }
1201 if (child == childInGroup) {
1202 return position;
1203 }
1204 if (notGone) {
1205 position += child.getIntrinsicHeight();
1206 }
1207 }
1208 return 0;
1209 }
Selim Cinek0242fbb2016-10-19 13:38:32 -07001210
1211 public void setIconsVisible(boolean iconsVisible) {
1212 if (mNotificationHeaderWrapper != null) {
1213 NotificationHeaderView header = mNotificationHeaderWrapper.getNotificationHeader();
1214 if (header != null) {
1215 header.getIcon().setForceHidden(!iconsVisible);
1216 }
1217 }
Selim Cinek414ad332017-02-24 19:06:12 -08001218 if (mNotificationHeaderWrapperLowPriority != null) {
1219 NotificationHeaderView header
1220 = mNotificationHeaderWrapperLowPriority.getNotificationHeader();
1221 if (header != null) {
1222 header.getIcon().setForceHidden(!iconsVisible);
1223 }
1224 }
Selim Cinek0242fbb2016-10-19 13:38:32 -07001225 }
Selim Cinekb3dadcc2016-11-21 17:21:13 -08001226
1227 public void setClipBottomAmount(int clipBottomAmount) {
1228 mClipBottomAmount = clipBottomAmount;
1229 updateChildrenClipping();
1230 }
Selim Cinek6743c0b2017-01-18 18:24:01 -08001231
1232 public void setIsLowPriority(boolean isLowPriority) {
1233 mIsLowPriority = isLowPriority;
Selim Cinek414ad332017-02-24 19:06:12 -08001234 if (mContainingNotification != null) { /* we're not yet set up yet otherwise */
1235 recreateLowPriorityHeader(null /* existingBuilder */);
1236 updateHeaderVisibility(false /* animate */);
1237 }
1238 if (mUserLocked) {
1239 setUserLocked(mUserLocked);
1240 }
1241 }
1242
1243 public NotificationHeaderView getVisibleHeader() {
1244 NotificationHeaderView header = mNotificationHeader;
1245 if (showingAsLowPriority()) {
1246 header = mNotificationHeaderLowPriority;
1247 }
1248 return header;
1249 }
1250
1251 public void onExpansionChanged() {
1252 if (mIsLowPriority) {
1253 if (mUserLocked) {
1254 setUserLocked(mUserLocked);
1255 }
1256 updateHeaderVisibility(true /* animate */);
1257 }
1258 }
1259
1260 public float getIncreasedPaddingAmount() {
1261 if (showingAsLowPriority()) {
1262 return 0.0f;
1263 }
1264 return getGroupExpandFraction();
Selim Cinek6743c0b2017-01-18 18:24:01 -08001265 }
Selim Cinek817abe72017-05-24 11:08:55 -07001266
1267 @VisibleForTesting
1268 public boolean isUserLocked() {
1269 return mUserLocked;
1270 }
Selim Cinek2871bef2017-11-22 08:40:00 -08001271
1272 public void setCurrentBottomRoundness(float currentBottomRoundness) {
1273 boolean last = true;
1274 for (int i = mChildren.size() - 1; i >= 0; i--) {
1275 ExpandableNotificationRow child = mChildren.get(i);
1276 if (child.getVisibility() == View.GONE) {
1277 continue;
1278 }
1279 float bottomRoundness = last ? currentBottomRoundness : 0.0f;
1280 child.setBottomRoundness(bottomRoundness, isShown() /* animate */);
1281 last = false;
1282 }
1283 }
Selim Cinekb5605e52015-02-20 18:21:41 +01001284}