blob: 030c8b737e844682cf074d11dbb595aa82cdac05 [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
19import android.content.Context;
Selim Cineka3d3b912016-02-02 11:22:06 -080020import android.content.res.Configuration;
Selim Cinekb5605e52015-02-20 18:21:41 +010021import android.util.AttributeSet;
22import android.view.LayoutInflater;
23import android.view.View;
24import android.view.ViewGroup;
Selim Cinekb5605e52015-02-20 18:21:41 +010025
26import com.android.systemui.R;
Selim Cinek9c7712d2015-12-08 19:19:48 -080027import com.android.systemui.ViewInvertHelper;
Selim Cinek7b73a4c2016-01-12 18:32:11 -080028import com.android.systemui.statusbar.CrossFadeHelper;
Selim Cinekb5605e52015-02-20 18:21:41 +010029import com.android.systemui.statusbar.ExpandableNotificationRow;
Selim Cinek83bc7832015-10-22 13:26:54 -070030import com.android.systemui.statusbar.notification.HybridNotificationView;
31import com.android.systemui.statusbar.notification.HybridNotificationViewManager;
Selim Cinek42357e02016-02-24 18:48:01 -080032import com.android.systemui.statusbar.notification.NotificationUtils;
Selim Cinek9c7712d2015-12-08 19:19:48 -080033import com.android.systemui.statusbar.phone.NotificationPanelView;
Selim Cinekb5605e52015-02-20 18:21:41 +010034
35import java.util.ArrayList;
36import java.util.List;
37
38/**
39 * A container containing child notifications
40 */
41public class NotificationChildrenContainer extends ViewGroup {
42
Selim Cinek83bc7832015-10-22 13:26:54 -070043 private static final int NUMBER_OF_CHILDREN_WHEN_COLLAPSED = 2;
44 private static final int NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED = 5;
45 private static final int NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED = 8;
46
Selim Cinekb5605e52015-02-20 18:21:41 +010047 private final List<View> mDividers = new ArrayList<>();
48 private final List<ExpandableNotificationRow> mChildren = new ArrayList<>();
Selim Cinek83bc7832015-10-22 13:26:54 -070049 private final HybridNotificationViewManager mHybridViewManager;
Selim Cinek01af3342016-02-09 19:25:31 -080050 private int mChildPadding;
51 private int mDividerHeight;
52 private int mMaxNotificationHeight;
53 private int mNotificationHeaderHeight;
54 private int mNotificationAppearDistance;
55 private int mNotificatonTopPadding;
56 private float mCollapsedBottompadding;
Selim Cinek9c7712d2015-12-08 19:19:48 -080057 private ViewInvertHelper mOverflowInvertHelper;
Selim Cinek83bc7832015-10-22 13:26:54 -070058 private boolean mChildrenExpanded;
59 private ExpandableNotificationRow mNotificationParent;
60 private HybridNotificationView mGroupOverflowContainer;
61 private ViewState mGroupOverFlowState;
Selim Cineka69f2a62015-12-11 17:28:12 -080062 private int mRealHeight;
Selim Cineka3d3b912016-02-02 11:22:06 -080063 private int mLayoutDirection = LAYOUT_DIRECTION_UNDEFINED;
Selim Cinek42357e02016-02-24 18:48:01 -080064 private boolean mUserLocked;
65 private int mActualHeight;
Selim Cinekb5605e52015-02-20 18:21:41 +010066
67 public NotificationChildrenContainer(Context context) {
68 this(context, null);
69 }
70
71 public NotificationChildrenContainer(Context context, AttributeSet attrs) {
72 this(context, attrs, 0);
73 }
74
75 public NotificationChildrenContainer(Context context, AttributeSet attrs, int defStyleAttr) {
76 this(context, attrs, defStyleAttr, 0);
77 }
78
79 public NotificationChildrenContainer(Context context, AttributeSet attrs, int defStyleAttr,
80 int defStyleRes) {
81 super(context, attrs, defStyleAttr, defStyleRes);
Selim Cinek01af3342016-02-09 19:25:31 -080082 initDimens();
83 mHybridViewManager = new HybridNotificationViewManager(getContext(), this);
84 }
85
86 private void initDimens() {
Selim Cinekb5605e52015-02-20 18:21:41 +010087 mChildPadding = getResources().getDimensionPixelSize(
88 R.dimen.notification_children_padding);
Selim Cinekcacc6042016-01-21 16:16:41 -080089 mDividerHeight = Math.max(1, getResources().getDimensionPixelSize(
90 R.dimen.notification_divider_height));
Selim Cinekb5605e52015-02-20 18:21:41 +010091 mMaxNotificationHeight = getResources().getDimensionPixelSize(
92 R.dimen.notification_max_height);
93 mNotificationAppearDistance = getResources().getDimensionPixelSize(
94 R.dimen.notification_appear_distance);
Selim Cinekeef84282015-10-30 16:28:00 -070095 mNotificationHeaderHeight = getResources().getDimensionPixelSize(
Selim Cinek7b836392015-12-04 20:02:59 -080096 com.android.internal.R.dimen.notification_content_margin_top);
97 mNotificatonTopPadding = getResources().getDimensionPixelSize(
98 R.dimen.notification_children_container_top_padding);
99 mCollapsedBottompadding = 11.5f * getResources().getDisplayMetrics().density;
Selim Cinekb5605e52015-02-20 18:21:41 +0100100 }
101
102 @Override
103 protected void onLayout(boolean changed, int l, int t, int r, int b) {
Selim Cinek471e31a2015-12-11 13:39:48 -0800104 int childCount = Math.min(mChildren.size(), NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
Selim Cinekb5605e52015-02-20 18:21:41 +0100105 for (int i = 0; i < childCount; i++) {
106 View child = mChildren.get(i);
Selim Cinek7b836392015-12-04 20:02:59 -0800107 if (child.getVisibility() == View.GONE) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100108 continue;
109 }
110 child.layout(0, 0, getWidth(), child.getMeasuredHeight());
Selim Cinek7b836392015-12-04 20:02:59 -0800111 mDividers.get(i).layout(0, 0, getWidth(), mDividerHeight);
Selim Cinekb5605e52015-02-20 18:21:41 +0100112 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700113 if (mGroupOverflowContainer != null) {
114 mGroupOverflowContainer.layout(0, 0, getWidth(),
115 mGroupOverflowContainer.getMeasuredHeight());
116 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100117 }
118
119 @Override
120 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
121 int ownMaxHeight = mMaxNotificationHeight;
122 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
123 boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
124 boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST;
Selim Cineka69f2a62015-12-11 17:28:12 -0800125 int size = MeasureSpec.getSize(heightMeasureSpec);
Selim Cinekb5605e52015-02-20 18:21:41 +0100126 if (hasFixedHeight || isHeightLimited) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100127 ownMaxHeight = Math.min(ownMaxHeight, size);
128 }
129 int newHeightSpec = MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.AT_MOST);
130 int dividerHeightSpec = MeasureSpec.makeMeasureSpec(mDividerHeight, MeasureSpec.EXACTLY);
Selim Cinek7b836392015-12-04 20:02:59 -0800131 int height = mNotificationHeaderHeight + mNotificatonTopPadding;
Selim Cinek471e31a2015-12-11 13:39:48 -0800132 int childCount = Math.min(mChildren.size(), NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
Selim Cinekb5605e52015-02-20 18:21:41 +0100133 for (int i = 0; i < childCount; i++) {
134 View child = mChildren.get(i);
Selim Cinekb5605e52015-02-20 18:21:41 +0100135 child.measure(widthMeasureSpec, newHeightSpec);
136 height += child.getMeasuredHeight();
Selim Cinek7b836392015-12-04 20:02:59 -0800137
138 // layout the divider
139 View divider = mDividers.get(i);
140 divider.measure(widthMeasureSpec, dividerHeightSpec);
141 height += mDividerHeight;
Selim Cinekb5605e52015-02-20 18:21:41 +0100142 }
143 int width = MeasureSpec.getSize(widthMeasureSpec);
Selim Cinek83bc7832015-10-22 13:26:54 -0700144 if (mGroupOverflowContainer != null) {
145 mGroupOverflowContainer.measure(widthMeasureSpec, newHeightSpec);
146 }
Selim Cineka69f2a62015-12-11 17:28:12 -0800147 mRealHeight = height;
148 if (heightMode != MeasureSpec.UNSPECIFIED) {
149 height = Math.min(height, size);
150 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100151 setMeasuredDimension(width, height);
152 }
153
Selim Cineka69f2a62015-12-11 17:28:12 -0800154 @Override
155 public boolean pointInView(float localX, float localY, float slop) {
156 return localX >= -slop && localY >= -slop && localX < ((mRight - mLeft) + slop) &&
157 localY < (mRealHeight + slop);
158 }
159
Selim Cinekb5605e52015-02-20 18:21:41 +0100160 /**
161 * Add a child notification to this view.
162 *
163 * @param row the row to add
164 * @param childIndex the index to add it at, if -1 it will be added at the end
165 */
166 public void addNotification(ExpandableNotificationRow row, int childIndex) {
167 int newIndex = childIndex < 0 ? mChildren.size() : childIndex;
168 mChildren.add(newIndex, row);
169 addView(row);
Selim Cinek7b836392015-12-04 20:02:59 -0800170
171 View divider = inflateDivider();
172 addView(divider);
173 mDividers.add(newIndex, divider);
174
Selim Cinek83bc7832015-10-22 13:26:54 -0700175 updateGroupOverflow();
Selim Cinekb5605e52015-02-20 18:21:41 +0100176 }
177
178 public void removeNotification(ExpandableNotificationRow row) {
179 int childIndex = mChildren.indexOf(row);
180 mChildren.remove(row);
181 removeView(row);
Selim Cinek7b836392015-12-04 20:02:59 -0800182
Selim Cinek7b73a4c2016-01-12 18:32:11 -0800183 final View divider = mDividers.remove(childIndex);
Selim Cinek7b836392015-12-04 20:02:59 -0800184 removeView(divider);
Selim Cinek7b73a4c2016-01-12 18:32:11 -0800185 getOverlay().add(divider);
186 CrossFadeHelper.fadeOut(divider, new Runnable() {
187 @Override
188 public void run() {
189 getOverlay().remove(divider);
190 }
191 });
Selim Cinek7b836392015-12-04 20:02:59 -0800192
Selim Cinekb5605e52015-02-20 18:21:41 +0100193 row.setSystemChildExpanded(false);
Selim Cinek83bc7832015-10-22 13:26:54 -0700194 updateGroupOverflow();
195 }
196
197 public void updateGroupOverflow() {
198 int childCount = mChildren.size();
199 int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
200 boolean hasOverflow = childCount > maxAllowedVisibleChildren;
201 int lastVisibleIndex = hasOverflow ? maxAllowedVisibleChildren - 2
202 : maxAllowedVisibleChildren - 1;
203 if (hasOverflow) {
204 mGroupOverflowContainer = mHybridViewManager.bindFromNotificationGroup(
205 mGroupOverflowContainer, mChildren, lastVisibleIndex + 1);
Selim Cinek9c7712d2015-12-08 19:19:48 -0800206 if (mOverflowInvertHelper == null) {
207 mOverflowInvertHelper= new ViewInvertHelper(mGroupOverflowContainer,
208 NotificationPanelView.DOZE_ANIMATION_DURATION);
209 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700210 if (mGroupOverFlowState == null) {
211 mGroupOverFlowState = new ViewState();
212 }
213 } else if (mGroupOverflowContainer != null) {
214 removeView(mGroupOverflowContainer);
215 mGroupOverflowContainer = null;
Selim Cinek9c7712d2015-12-08 19:19:48 -0800216 mOverflowInvertHelper = null;
Selim Cinek83bc7832015-10-22 13:26:54 -0700217 mGroupOverFlowState = null;
218 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100219 }
220
Selim Cineka3d3b912016-02-02 11:22:06 -0800221 @Override
222 protected void onConfigurationChanged(Configuration newConfig) {
223 super.onConfigurationChanged(newConfig);
224 int layoutDirection = getLayoutDirection();
225 if (layoutDirection != mLayoutDirection) {
226 updateGroupOverflow();
227 mLayoutDirection = layoutDirection;
228 }
229 }
230
Selim Cinekb5605e52015-02-20 18:21:41 +0100231 private View inflateDivider() {
232 return LayoutInflater.from(mContext).inflate(
233 R.layout.notification_children_divider, this, false);
234 }
235
236 public List<ExpandableNotificationRow> getNotificationChildren() {
237 return mChildren;
238 }
239
240 /**
241 * Apply the order given in the list to the children.
242 *
243 * @param childOrder the new list order
244 * @return whether the list order has changed
245 */
246 public boolean applyChildOrder(List<ExpandableNotificationRow> childOrder) {
247 if (childOrder == null) {
248 return false;
249 }
250 boolean result = false;
251 for (int i = 0; i < mChildren.size() && i < childOrder.size(); i++) {
252 ExpandableNotificationRow child = mChildren.get(i);
253 ExpandableNotificationRow desiredChild = childOrder.get(i);
254 if (child != desiredChild) {
255 mChildren.remove(desiredChild);
256 mChildren.add(i, desiredChild);
257 result = true;
258 }
259 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700260 updateExpansionStates();
Selim Cinekb5605e52015-02-20 18:21:41 +0100261 return result;
262 }
263
Selim Cinek83bc7832015-10-22 13:26:54 -0700264 private void updateExpansionStates() {
265 // Let's make the first child expanded if the parent is
266 for (int i = 0; i < mChildren.size(); i++) {
267 ExpandableNotificationRow child = mChildren.get(i);
268 child.setSystemChildExpanded(false);
269 }
270 }
271
272 /**
273 *
274 * @return the intrinsic size of this children container, i.e the natural fully expanded state
275 */
Selim Cinekb5605e52015-02-20 18:21:41 +0100276 public int getIntrinsicHeight() {
Selim Cinek83bc7832015-10-22 13:26:54 -0700277 int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren();
278 return getIntrinsicHeight(maxAllowedVisibleChildren);
279 }
280
281 /**
282 * @return the intrinsic height with a number of children given
283 * in @param maxAllowedVisibleChildren
284 */
285 private int getIntrinsicHeight(float maxAllowedVisibleChildren) {
Selim Cinek7b836392015-12-04 20:02:59 -0800286 int intrinsicHeight = mNotificationHeaderHeight;
Selim Cinekb5605e52015-02-20 18:21:41 +0100287 int visibleChildren = 0;
Selim Cinek83bc7832015-10-22 13:26:54 -0700288 int childCount = mChildren.size();
Selim Cinek42357e02016-02-24 18:48:01 -0800289 boolean firstChild = true;
290 float expandFactor = 0;
291 if (mUserLocked) {
292 expandFactor = getChildExpandFraction();
293 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100294 for (int i = 0; i < childCount; i++) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700295 if (visibleChildren >= maxAllowedVisibleChildren) {
296 break;
297 }
Selim Cinek42357e02016-02-24 18:48:01 -0800298 if (!firstChild) {
299 if (mUserLocked) {
300 intrinsicHeight += NotificationUtils.interpolate(mChildPadding, mDividerHeight,
301 expandFactor);
302 } else {
303 intrinsicHeight += mChildrenExpanded ? mDividerHeight : mChildPadding;
304 }
305 } else {
306 if (mUserLocked) {
307 intrinsicHeight += NotificationUtils.interpolate(
308 0,
309 mNotificatonTopPadding + mDividerHeight,
310 expandFactor);
311 } else {
312 intrinsicHeight += mChildrenExpanded
313 ? mNotificatonTopPadding + mDividerHeight
314 : 0;
315 }
316 firstChild = false;
317 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100318 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinekb5605e52015-02-20 18:21:41 +0100319 intrinsicHeight += child.getIntrinsicHeight();
320 visibleChildren++;
321 }
Selim Cinek42357e02016-02-24 18:48:01 -0800322 if (mUserLocked) {
323 intrinsicHeight += NotificationUtils.interpolate(mCollapsedBottompadding, 0.0f,
324 expandFactor);
325 } else if (!mChildrenExpanded) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700326 intrinsicHeight += mCollapsedBottompadding;
327 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100328 return intrinsicHeight;
329 }
330
331 /**
332 * Update the state of all its children based on a linear layout algorithm.
333 *
334 * @param resultState the state to update
335 * @param parentState the state of the parent
336 */
337 public void getState(StackScrollState resultState, StackViewState parentState) {
338 int childCount = mChildren.size();
Selim Cinekeef84282015-10-30 16:28:00 -0700339 int yPosition = mNotificationHeaderHeight;
Selim Cinekb5605e52015-02-20 18:21:41 +0100340 boolean firstChild = true;
Selim Cinek83bc7832015-10-22 13:26:54 -0700341 int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren();
342 boolean hasOverflow = !mChildrenExpanded && childCount > maxAllowedVisibleChildren
343 && maxAllowedVisibleChildren != NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED;
344 int lastVisibleIndex = hasOverflow
345 ? maxAllowedVisibleChildren - 2
346 : maxAllowedVisibleChildren - 1;
Selim Cinek42357e02016-02-24 18:48:01 -0800347 float expandFactor = 0;
348 if (mUserLocked) {
349 expandFactor = getChildExpandFraction();
350 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100351 for (int i = 0; i < childCount; i++) {
352 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinekb5605e52015-02-20 18:21:41 +0100353 if (!firstChild) {
Selim Cinek42357e02016-02-24 18:48:01 -0800354 if (mUserLocked) {
355 yPosition += NotificationUtils.interpolate(mChildPadding, mDividerHeight,
356 expandFactor);
357 } else {
358 yPosition += mChildrenExpanded ? mDividerHeight : mChildPadding;
359 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100360 } else {
Selim Cinek42357e02016-02-24 18:48:01 -0800361 if (mUserLocked) {
362 yPosition += NotificationUtils.interpolate(
363 0,
364 mNotificatonTopPadding + mDividerHeight,
365 expandFactor);
366 } else {
367 yPosition += mChildrenExpanded ? mNotificatonTopPadding + mDividerHeight : 0;
368 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100369 firstChild = false;
370 }
371 StackViewState childState = resultState.getViewStateForView(child);
372 int intrinsicHeight = child.getIntrinsicHeight();
373 childState.yTranslation = yPosition;
374 childState.zTranslation = 0;
375 childState.height = intrinsicHeight;
376 childState.dimmed = parentState.dimmed;
377 childState.dark = parentState.dark;
378 childState.hideSensitive = parentState.hideSensitive;
379 childState.belowSpeedBump = parentState.belowSpeedBump;
Selim Cinekb5605e52015-02-20 18:21:41 +0100380 childState.clipTopAmount = 0;
381 childState.topOverLap = 0;
Selim Cinek83bc7832015-10-22 13:26:54 -0700382 boolean visible = i <= lastVisibleIndex;
383 childState.alpha = visible ? 1 : 0;
Selim Cinekb5605e52015-02-20 18:21:41 +0100384 childState.location = parentState.location;
385 yPosition += intrinsicHeight;
386 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700387 if (mGroupOverflowContainer != null) {
388 mGroupOverFlowState.initFrom(mGroupOverflowContainer);
389 if (hasOverflow) {
390 StackViewState firstOverflowState =
391 resultState.getViewStateForView(mChildren.get(lastVisibleIndex + 1));
392 mGroupOverFlowState.yTranslation = firstOverflowState.yTranslation;
393 }
394 mGroupOverFlowState.alpha = mChildrenExpanded || !hasOverflow ? 0.0f : 1.0f;
395 }
396 }
397
398 private int getMaxAllowedVisibleChildren() {
399 return getMaxAllowedVisibleChildren(false /* likeCollapsed */);
400 }
401
402 private int getMaxAllowedVisibleChildren(boolean likeCollapsed) {
403 if (!likeCollapsed && (mChildrenExpanded || mNotificationParent.isUserLocked())) {
404 return NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED;
405 }
Selim Cinek77019c72015-12-09 10:18:02 -0800406 if (mNotificationParent.isExpanded() || mNotificationParent.isHeadsUp()) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700407 return NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED;
408 }
409 return NUMBER_OF_CHILDREN_WHEN_COLLAPSED;
Selim Cinekb5605e52015-02-20 18:21:41 +0100410 }
411
412 public void applyState(StackScrollState state) {
413 int childCount = mChildren.size();
Selim Cinek83bc7832015-10-22 13:26:54 -0700414 ViewState tmpState = new ViewState();
Selim Cinek42357e02016-02-24 18:48:01 -0800415 float expandFraction = getChildExpandFraction();
Selim Cinekb5605e52015-02-20 18:21:41 +0100416 for (int i = 0; i < childCount; i++) {
417 ExpandableNotificationRow child = mChildren.get(i);
418 StackViewState viewState = state.getViewStateForView(child);
Selim Cinekb5605e52015-02-20 18:21:41 +0100419 state.applyState(child, viewState);
Selim Cinek7b836392015-12-04 20:02:59 -0800420
421 // layout the divider
422 View divider = mDividers.get(i);
423 tmpState.initFrom(divider);
424 tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
Selim Cinek42357e02016-02-24 18:48:01 -0800425 float alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
426 if (mUserLocked && viewState.alpha != 0) {
427 alpha = NotificationUtils.interpolate(0, 0.5f, expandFraction);
428 }
429 tmpState.alpha = alpha;
Selim Cinek7b836392015-12-04 20:02:59 -0800430 state.applyViewState(divider, tmpState);
Selim Cinekb5605e52015-02-20 18:21:41 +0100431 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700432 if (mGroupOverflowContainer != null) {
433 state.applyViewState(mGroupOverflowContainer, mGroupOverFlowState);
434 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100435 }
436
Selim Cinekb5605e52015-02-20 18:21:41 +0100437 /**
438 * This is called when the children expansion has changed and positions the children properly
439 * for an appear animation.
440 *
441 * @param state the new state we animate to
442 */
443 public void prepareExpansionChanged(StackScrollState state) {
Selim Cinek277a8aa2016-01-22 12:12:37 -0800444 // TODO: do something that makes sense, like placing the invisible views correctly
445 return;
Selim Cinekb5605e52015-02-20 18:21:41 +0100446 }
447
448 public void startAnimationToState(StackScrollState state, StackStateAnimator stateAnimator,
Selim Cinek99695592016-01-12 17:51:35 -0800449 long baseDelay, long duration) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100450 int childCount = mChildren.size();
Selim Cinek83bc7832015-10-22 13:26:54 -0700451 ViewState tmpState = new ViewState();
Selim Cinek42357e02016-02-24 18:48:01 -0800452 float expandFraction = getChildExpandFraction();
Selim Cineke8126522015-12-08 21:21:17 -0800453 for (int i = childCount - 1; i >= 0; i--) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100454 ExpandableNotificationRow child = mChildren.get(i);
455 StackViewState viewState = state.getViewStateForView(child);
Selim Cinek99695592016-01-12 17:51:35 -0800456 stateAnimator.startStackAnimations(child, viewState, state, -1, baseDelay);
Selim Cinek7b836392015-12-04 20:02:59 -0800457
458 // layout the divider
459 View divider = mDividers.get(i);
460 tmpState.initFrom(divider);
461 tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
Selim Cinek42357e02016-02-24 18:48:01 -0800462 float alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
463 if (mUserLocked && viewState.alpha != 0) {
464 alpha = NotificationUtils.interpolate(0, 0.5f, expandFraction);
465 }
466 tmpState.alpha = alpha;
Selim Cinek99695592016-01-12 17:51:35 -0800467 stateAnimator.startViewAnimations(divider, tmpState, baseDelay, duration);
Selim Cinekb5605e52015-02-20 18:21:41 +0100468 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700469 if (mGroupOverflowContainer != null) {
Selim Cineke8126522015-12-08 21:21:17 -0800470 stateAnimator.startViewAnimations(mGroupOverflowContainer, mGroupOverFlowState,
471 baseDelay, duration);
Selim Cinek83bc7832015-10-22 13:26:54 -0700472 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100473 }
474
475 public ExpandableNotificationRow getViewAtPosition(float y) {
476 // find the view under the pointer, accounting for GONE views
477 final int count = mChildren.size();
478 for (int childIdx = 0; childIdx < count; childIdx++) {
479 ExpandableNotificationRow slidingChild = mChildren.get(childIdx);
480 float childTop = slidingChild.getTranslationY();
481 float top = childTop + slidingChild.getClipTopAmount();
482 float bottom = childTop + slidingChild.getActualHeight();
483 if (y >= top && y <= bottom) {
484 return slidingChild;
485 }
486 }
487 return null;
488 }
489
Selim Cinek83bc7832015-10-22 13:26:54 -0700490 public void setChildrenExpanded(boolean childrenExpanded) {
491 mChildrenExpanded = childrenExpanded;
492 }
493
Selim Cinek388df6d2015-10-22 13:25:11 -0700494 public void setNotificationParent(ExpandableNotificationRow parent) {
495 mNotificationParent = parent;
496 }
497
Selim Cinek83bc7832015-10-22 13:26:54 -0700498 public int getMaxContentHeight() {
Selim Cinek42357e02016-02-24 18:48:01 -0800499 int maxContentHeight = mNotificationHeaderHeight + mNotificatonTopPadding;
500 int visibleChildren = 0;
501 int childCount = mChildren.size();
502 for (int i = 0; i < childCount; i++) {
503 if (visibleChildren >= NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED) {
504 break;
505 }
506 ExpandableNotificationRow child = mChildren.get(i);
507 float childHeight = child.isExpanded()
508 ? child.getMaxExpandHeight()
509 : child.getShowingLayout().getMinHeight(true /* likeGroupExpanded */);
510 maxContentHeight += childHeight;
511 visibleChildren++;
512 }
513 if (visibleChildren > 0) {
514 maxContentHeight += visibleChildren * mDividerHeight;
515 }
516 return maxContentHeight;
517 }
518
519 public void setActualHeight(int actualHeight) {
520 if (!mUserLocked) {
521 return;
522 }
523 mActualHeight = actualHeight;
524 float fraction = getChildExpandFraction();
525 int childCount = mChildren.size();
526 for (int i = 0; i < childCount; i++) {
527 ExpandableNotificationRow child = mChildren.get(i);
528 float childHeight = child.isExpanded()
529 ? child.getMaxExpandHeight()
530 : child.getShowingLayout().getMinHeight(true /* likeGroupExpanded */);
531 float singleLineHeight = child.getShowingLayout().getMinHeight(
532 false /* likeGroupExpanded */);
533 child.setActualHeight((int) NotificationUtils.interpolate(singleLineHeight, childHeight,
534 fraction), false);
535 }
536 }
537
538 public float getChildExpandFraction() {
539 int allChildrenVisibleHeight = getChildrenExpandStartHeight();
540 int maxContentHeight = getMaxContentHeight();
541 float factor = (mActualHeight - allChildrenVisibleHeight)
542 / (float) (maxContentHeight - allChildrenVisibleHeight);
543 return Math.max(0.0f, Math.min(1.0f, factor));
544 }
545
546 private int getChildrenExpandStartHeight() {
547 int intrinsicHeight = mNotificationHeaderHeight;
548 int visibleChildren = 0;
549 int childCount = mChildren.size();
550 for (int i = 0; i < childCount; i++) {
551 if (visibleChildren >= NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED) {
552 break;
553 }
554 ExpandableNotificationRow child = mChildren.get(i);
555 intrinsicHeight += child.getMinHeight();
556 visibleChildren++;
557 }
558 if (visibleChildren > 0) {
559 intrinsicHeight += (visibleChildren - 1) * mChildPadding;
560 }
561 intrinsicHeight += mCollapsedBottompadding;
562 return intrinsicHeight;
Selim Cinek83bc7832015-10-22 13:26:54 -0700563 }
564
565 public int getMinHeight() {
Selim Cinekb55386d2015-12-16 17:26:49 -0800566 return getIntrinsicHeight(NUMBER_OF_CHILDREN_WHEN_COLLAPSED);
567 }
568
569 public int getMinExpandHeight() {
Selim Cinek83bc7832015-10-22 13:26:54 -0700570 return getIntrinsicHeight(getMaxAllowedVisibleChildren(true /* forceCollapsed */));
Selim Cinekb5605e52015-02-20 18:21:41 +0100571 }
Selim Cinek9c7712d2015-12-08 19:19:48 -0800572
573 public void setDark(boolean dark, boolean fade, long delay) {
574 if (mGroupOverflowContainer != null) {
575 mOverflowInvertHelper.setInverted(dark, fade, delay);
576 }
577 }
Selim Cinek01af3342016-02-09 19:25:31 -0800578
579 public void reInflateViews() {
580 initDimens();
581 for (int i = 0; i < mDividers.size(); i++) {
582 View prevDivider = mDividers.get(i);
583 int index = indexOfChild(prevDivider);
584 removeView(prevDivider);
585 View divider = inflateDivider();
586 addView(divider, index);
587 mDividers.set(i, divider);
588 }
589 }
Selim Cinek42357e02016-02-24 18:48:01 -0800590
591 public void setUserLocked(boolean userLocked) {
592 mUserLocked = userLocked;
593 int childCount = mChildren.size();
594 for (int i = 0; i < childCount; i++) {
595 ExpandableNotificationRow child = mChildren.get(i);
596 child.setUserLocked(userLocked);
597 }
598 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100599}