blob: 795573352ba8c01ecfa769f314b847148a91e454 [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 Cinek8e0c4982016-02-29 20:03:08 -0800170 row.setUserLocked(mUserLocked);
Selim Cinek7b836392015-12-04 20:02:59 -0800171
172 View divider = inflateDivider();
173 addView(divider);
174 mDividers.add(newIndex, divider);
175
Selim Cinek83bc7832015-10-22 13:26:54 -0700176 updateGroupOverflow();
Selim Cinekb5605e52015-02-20 18:21:41 +0100177 }
178
179 public void removeNotification(ExpandableNotificationRow row) {
180 int childIndex = mChildren.indexOf(row);
181 mChildren.remove(row);
182 removeView(row);
Selim Cinek7b836392015-12-04 20:02:59 -0800183
Selim Cinek7b73a4c2016-01-12 18:32:11 -0800184 final View divider = mDividers.remove(childIndex);
Selim Cinek7b836392015-12-04 20:02:59 -0800185 removeView(divider);
Selim Cinek7b73a4c2016-01-12 18:32:11 -0800186 getOverlay().add(divider);
187 CrossFadeHelper.fadeOut(divider, new Runnable() {
188 @Override
189 public void run() {
190 getOverlay().remove(divider);
191 }
192 });
Selim Cinek7b836392015-12-04 20:02:59 -0800193
Selim Cinekb5605e52015-02-20 18:21:41 +0100194 row.setSystemChildExpanded(false);
Selim Cinek8e0c4982016-02-29 20:03:08 -0800195 row.setUserLocked(false);
Selim Cinek83bc7832015-10-22 13:26:54 -0700196 updateGroupOverflow();
197 }
198
199 public void updateGroupOverflow() {
200 int childCount = mChildren.size();
201 int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
202 boolean hasOverflow = childCount > maxAllowedVisibleChildren;
203 int lastVisibleIndex = hasOverflow ? maxAllowedVisibleChildren - 2
204 : maxAllowedVisibleChildren - 1;
205 if (hasOverflow) {
206 mGroupOverflowContainer = mHybridViewManager.bindFromNotificationGroup(
207 mGroupOverflowContainer, mChildren, lastVisibleIndex + 1);
Selim Cinek9c7712d2015-12-08 19:19:48 -0800208 if (mOverflowInvertHelper == null) {
209 mOverflowInvertHelper= new ViewInvertHelper(mGroupOverflowContainer,
210 NotificationPanelView.DOZE_ANIMATION_DURATION);
211 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700212 if (mGroupOverFlowState == null) {
213 mGroupOverFlowState = new ViewState();
214 }
215 } else if (mGroupOverflowContainer != null) {
216 removeView(mGroupOverflowContainer);
217 mGroupOverflowContainer = null;
Selim Cinek9c7712d2015-12-08 19:19:48 -0800218 mOverflowInvertHelper = null;
Selim Cinek83bc7832015-10-22 13:26:54 -0700219 mGroupOverFlowState = null;
220 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100221 }
222
Selim Cineka3d3b912016-02-02 11:22:06 -0800223 @Override
224 protected void onConfigurationChanged(Configuration newConfig) {
225 super.onConfigurationChanged(newConfig);
226 int layoutDirection = getLayoutDirection();
227 if (layoutDirection != mLayoutDirection) {
228 updateGroupOverflow();
229 mLayoutDirection = layoutDirection;
230 }
231 }
232
Selim Cinekb5605e52015-02-20 18:21:41 +0100233 private View inflateDivider() {
234 return LayoutInflater.from(mContext).inflate(
235 R.layout.notification_children_divider, this, false);
236 }
237
238 public List<ExpandableNotificationRow> getNotificationChildren() {
239 return mChildren;
240 }
241
242 /**
243 * Apply the order given in the list to the children.
244 *
245 * @param childOrder the new list order
246 * @return whether the list order has changed
247 */
248 public boolean applyChildOrder(List<ExpandableNotificationRow> childOrder) {
249 if (childOrder == null) {
250 return false;
251 }
252 boolean result = false;
253 for (int i = 0; i < mChildren.size() && i < childOrder.size(); i++) {
254 ExpandableNotificationRow child = mChildren.get(i);
255 ExpandableNotificationRow desiredChild = childOrder.get(i);
256 if (child != desiredChild) {
257 mChildren.remove(desiredChild);
258 mChildren.add(i, desiredChild);
259 result = true;
260 }
261 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700262 updateExpansionStates();
Selim Cinekb5605e52015-02-20 18:21:41 +0100263 return result;
264 }
265
Selim Cinek83bc7832015-10-22 13:26:54 -0700266 private void updateExpansionStates() {
Selim Cinek898d1732016-02-29 19:57:35 -0800267 if (mChildrenExpanded || mUserLocked) {
268 // we don't modify it the group is expanded or if we are expanding it
269 return;
270 }
271 int size = mChildren.size();
272 for (int i = 0; i < size; i++) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700273 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinek898d1732016-02-29 19:57:35 -0800274 child.setSystemChildExpanded(i == 0 && size == 1);
Selim Cinek83bc7832015-10-22 13:26:54 -0700275 }
276 }
277
278 /**
279 *
280 * @return the intrinsic size of this children container, i.e the natural fully expanded state
281 */
Selim Cinekb5605e52015-02-20 18:21:41 +0100282 public int getIntrinsicHeight() {
Selim Cinek83bc7832015-10-22 13:26:54 -0700283 int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren();
284 return getIntrinsicHeight(maxAllowedVisibleChildren);
285 }
286
287 /**
288 * @return the intrinsic height with a number of children given
289 * in @param maxAllowedVisibleChildren
290 */
291 private int getIntrinsicHeight(float maxAllowedVisibleChildren) {
Selim Cinek7b836392015-12-04 20:02:59 -0800292 int intrinsicHeight = mNotificationHeaderHeight;
Selim Cinekb5605e52015-02-20 18:21:41 +0100293 int visibleChildren = 0;
Selim Cinek83bc7832015-10-22 13:26:54 -0700294 int childCount = mChildren.size();
Selim Cinek42357e02016-02-24 18:48:01 -0800295 boolean firstChild = true;
296 float expandFactor = 0;
297 if (mUserLocked) {
298 expandFactor = getChildExpandFraction();
299 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100300 for (int i = 0; i < childCount; i++) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700301 if (visibleChildren >= maxAllowedVisibleChildren) {
302 break;
303 }
Selim Cinek42357e02016-02-24 18:48:01 -0800304 if (!firstChild) {
305 if (mUserLocked) {
306 intrinsicHeight += NotificationUtils.interpolate(mChildPadding, mDividerHeight,
307 expandFactor);
308 } else {
309 intrinsicHeight += mChildrenExpanded ? mDividerHeight : mChildPadding;
310 }
311 } else {
312 if (mUserLocked) {
313 intrinsicHeight += NotificationUtils.interpolate(
314 0,
315 mNotificatonTopPadding + mDividerHeight,
316 expandFactor);
317 } else {
318 intrinsicHeight += mChildrenExpanded
319 ? mNotificatonTopPadding + mDividerHeight
320 : 0;
321 }
322 firstChild = false;
323 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100324 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinekb5605e52015-02-20 18:21:41 +0100325 intrinsicHeight += child.getIntrinsicHeight();
326 visibleChildren++;
327 }
Selim Cinek42357e02016-02-24 18:48:01 -0800328 if (mUserLocked) {
329 intrinsicHeight += NotificationUtils.interpolate(mCollapsedBottompadding, 0.0f,
330 expandFactor);
331 } else if (!mChildrenExpanded) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700332 intrinsicHeight += mCollapsedBottompadding;
333 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100334 return intrinsicHeight;
335 }
336
337 /**
338 * Update the state of all its children based on a linear layout algorithm.
339 *
340 * @param resultState the state to update
341 * @param parentState the state of the parent
342 */
343 public void getState(StackScrollState resultState, StackViewState parentState) {
344 int childCount = mChildren.size();
Selim Cinekeef84282015-10-30 16:28:00 -0700345 int yPosition = mNotificationHeaderHeight;
Selim Cinekb5605e52015-02-20 18:21:41 +0100346 boolean firstChild = true;
Selim Cinek83bc7832015-10-22 13:26:54 -0700347 int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren();
348 boolean hasOverflow = !mChildrenExpanded && childCount > maxAllowedVisibleChildren
349 && maxAllowedVisibleChildren != NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED;
350 int lastVisibleIndex = hasOverflow
351 ? maxAllowedVisibleChildren - 2
352 : maxAllowedVisibleChildren - 1;
Selim Cinek42357e02016-02-24 18:48:01 -0800353 float expandFactor = 0;
354 if (mUserLocked) {
355 expandFactor = getChildExpandFraction();
356 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100357 for (int i = 0; i < childCount; i++) {
358 ExpandableNotificationRow child = mChildren.get(i);
Selim Cinekb5605e52015-02-20 18:21:41 +0100359 if (!firstChild) {
Selim Cinek42357e02016-02-24 18:48:01 -0800360 if (mUserLocked) {
361 yPosition += NotificationUtils.interpolate(mChildPadding, mDividerHeight,
362 expandFactor);
363 } else {
364 yPosition += mChildrenExpanded ? mDividerHeight : mChildPadding;
365 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100366 } else {
Selim Cinek42357e02016-02-24 18:48:01 -0800367 if (mUserLocked) {
368 yPosition += NotificationUtils.interpolate(
369 0,
370 mNotificatonTopPadding + mDividerHeight,
371 expandFactor);
372 } else {
373 yPosition += mChildrenExpanded ? mNotificatonTopPadding + mDividerHeight : 0;
374 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100375 firstChild = false;
376 }
377 StackViewState childState = resultState.getViewStateForView(child);
378 int intrinsicHeight = child.getIntrinsicHeight();
379 childState.yTranslation = yPosition;
380 childState.zTranslation = 0;
381 childState.height = intrinsicHeight;
382 childState.dimmed = parentState.dimmed;
383 childState.dark = parentState.dark;
384 childState.hideSensitive = parentState.hideSensitive;
385 childState.belowSpeedBump = parentState.belowSpeedBump;
Selim Cinekb5605e52015-02-20 18:21:41 +0100386 childState.clipTopAmount = 0;
387 childState.topOverLap = 0;
Selim Cinek83bc7832015-10-22 13:26:54 -0700388 boolean visible = i <= lastVisibleIndex;
389 childState.alpha = visible ? 1 : 0;
Selim Cinekb5605e52015-02-20 18:21:41 +0100390 childState.location = parentState.location;
391 yPosition += intrinsicHeight;
392 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700393 if (mGroupOverflowContainer != null) {
394 mGroupOverFlowState.initFrom(mGroupOverflowContainer);
395 if (hasOverflow) {
396 StackViewState firstOverflowState =
397 resultState.getViewStateForView(mChildren.get(lastVisibleIndex + 1));
398 mGroupOverFlowState.yTranslation = firstOverflowState.yTranslation;
399 }
400 mGroupOverFlowState.alpha = mChildrenExpanded || !hasOverflow ? 0.0f : 1.0f;
401 }
402 }
403
404 private int getMaxAllowedVisibleChildren() {
405 return getMaxAllowedVisibleChildren(false /* likeCollapsed */);
406 }
407
408 private int getMaxAllowedVisibleChildren(boolean likeCollapsed) {
409 if (!likeCollapsed && (mChildrenExpanded || mNotificationParent.isUserLocked())) {
410 return NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED;
411 }
Selim Cinek77019c72015-12-09 10:18:02 -0800412 if (mNotificationParent.isExpanded() || mNotificationParent.isHeadsUp()) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700413 return NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED;
414 }
415 return NUMBER_OF_CHILDREN_WHEN_COLLAPSED;
Selim Cinekb5605e52015-02-20 18:21:41 +0100416 }
417
418 public void applyState(StackScrollState state) {
419 int childCount = mChildren.size();
Selim Cinek83bc7832015-10-22 13:26:54 -0700420 ViewState tmpState = new ViewState();
Selim Cinek42357e02016-02-24 18:48:01 -0800421 float expandFraction = getChildExpandFraction();
Selim Cinekb5605e52015-02-20 18:21:41 +0100422 for (int i = 0; i < childCount; i++) {
423 ExpandableNotificationRow child = mChildren.get(i);
424 StackViewState viewState = state.getViewStateForView(child);
Selim Cinekb5605e52015-02-20 18:21:41 +0100425 state.applyState(child, viewState);
Selim Cinek7b836392015-12-04 20:02:59 -0800426
427 // layout the divider
428 View divider = mDividers.get(i);
429 tmpState.initFrom(divider);
430 tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
Selim Cinek42357e02016-02-24 18:48:01 -0800431 float alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
432 if (mUserLocked && viewState.alpha != 0) {
433 alpha = NotificationUtils.interpolate(0, 0.5f, expandFraction);
434 }
435 tmpState.alpha = alpha;
Selim Cinek7b836392015-12-04 20:02:59 -0800436 state.applyViewState(divider, tmpState);
Selim Cinekb5605e52015-02-20 18:21:41 +0100437 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700438 if (mGroupOverflowContainer != null) {
439 state.applyViewState(mGroupOverflowContainer, mGroupOverFlowState);
440 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100441 }
442
Selim Cinekb5605e52015-02-20 18:21:41 +0100443 /**
444 * This is called when the children expansion has changed and positions the children properly
445 * for an appear animation.
446 *
447 * @param state the new state we animate to
448 */
449 public void prepareExpansionChanged(StackScrollState state) {
Selim Cinek277a8aa2016-01-22 12:12:37 -0800450 // TODO: do something that makes sense, like placing the invisible views correctly
451 return;
Selim Cinekb5605e52015-02-20 18:21:41 +0100452 }
453
454 public void startAnimationToState(StackScrollState state, StackStateAnimator stateAnimator,
Selim Cinek99695592016-01-12 17:51:35 -0800455 long baseDelay, long duration) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100456 int childCount = mChildren.size();
Selim Cinek83bc7832015-10-22 13:26:54 -0700457 ViewState tmpState = new ViewState();
Selim Cinek42357e02016-02-24 18:48:01 -0800458 float expandFraction = getChildExpandFraction();
Selim Cineke8126522015-12-08 21:21:17 -0800459 for (int i = childCount - 1; i >= 0; i--) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100460 ExpandableNotificationRow child = mChildren.get(i);
461 StackViewState viewState = state.getViewStateForView(child);
Selim Cinek99695592016-01-12 17:51:35 -0800462 stateAnimator.startStackAnimations(child, viewState, state, -1, baseDelay);
Selim Cinek7b836392015-12-04 20:02:59 -0800463
464 // layout the divider
465 View divider = mDividers.get(i);
466 tmpState.initFrom(divider);
467 tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
Selim Cinek42357e02016-02-24 18:48:01 -0800468 float alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
469 if (mUserLocked && viewState.alpha != 0) {
470 alpha = NotificationUtils.interpolate(0, 0.5f, expandFraction);
471 }
472 tmpState.alpha = alpha;
Selim Cinek99695592016-01-12 17:51:35 -0800473 stateAnimator.startViewAnimations(divider, tmpState, baseDelay, duration);
Selim Cinekb5605e52015-02-20 18:21:41 +0100474 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700475 if (mGroupOverflowContainer != null) {
Selim Cineke8126522015-12-08 21:21:17 -0800476 stateAnimator.startViewAnimations(mGroupOverflowContainer, mGroupOverFlowState,
477 baseDelay, duration);
Selim Cinek83bc7832015-10-22 13:26:54 -0700478 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100479 }
480
481 public ExpandableNotificationRow getViewAtPosition(float y) {
482 // find the view under the pointer, accounting for GONE views
483 final int count = mChildren.size();
484 for (int childIdx = 0; childIdx < count; childIdx++) {
485 ExpandableNotificationRow slidingChild = mChildren.get(childIdx);
486 float childTop = slidingChild.getTranslationY();
487 float top = childTop + slidingChild.getClipTopAmount();
488 float bottom = childTop + slidingChild.getActualHeight();
489 if (y >= top && y <= bottom) {
490 return slidingChild;
491 }
492 }
493 return null;
494 }
495
Selim Cinek83bc7832015-10-22 13:26:54 -0700496 public void setChildrenExpanded(boolean childrenExpanded) {
497 mChildrenExpanded = childrenExpanded;
Selim Cinek898d1732016-02-29 19:57:35 -0800498 updateExpansionStates();
Selim Cinek83bc7832015-10-22 13:26:54 -0700499 }
500
Selim Cinek388df6d2015-10-22 13:25:11 -0700501 public void setNotificationParent(ExpandableNotificationRow parent) {
502 mNotificationParent = parent;
503 }
504
Selim Cinek83bc7832015-10-22 13:26:54 -0700505 public int getMaxContentHeight() {
Selim Cinek42357e02016-02-24 18:48:01 -0800506 int maxContentHeight = mNotificationHeaderHeight + mNotificatonTopPadding;
507 int visibleChildren = 0;
508 int childCount = mChildren.size();
509 for (int i = 0; i < childCount; i++) {
510 if (visibleChildren >= NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED) {
511 break;
512 }
513 ExpandableNotificationRow child = mChildren.get(i);
514 float childHeight = child.isExpanded()
515 ? child.getMaxExpandHeight()
516 : child.getShowingLayout().getMinHeight(true /* likeGroupExpanded */);
517 maxContentHeight += childHeight;
518 visibleChildren++;
519 }
520 if (visibleChildren > 0) {
521 maxContentHeight += visibleChildren * mDividerHeight;
522 }
523 return maxContentHeight;
524 }
525
526 public void setActualHeight(int actualHeight) {
527 if (!mUserLocked) {
528 return;
529 }
530 mActualHeight = actualHeight;
531 float fraction = getChildExpandFraction();
532 int childCount = mChildren.size();
533 for (int i = 0; i < childCount; i++) {
534 ExpandableNotificationRow child = mChildren.get(i);
535 float childHeight = child.isExpanded()
536 ? child.getMaxExpandHeight()
537 : child.getShowingLayout().getMinHeight(true /* likeGroupExpanded */);
538 float singleLineHeight = child.getShowingLayout().getMinHeight(
539 false /* likeGroupExpanded */);
540 child.setActualHeight((int) NotificationUtils.interpolate(singleLineHeight, childHeight,
541 fraction), false);
542 }
543 }
544
545 public float getChildExpandFraction() {
546 int allChildrenVisibleHeight = getChildrenExpandStartHeight();
547 int maxContentHeight = getMaxContentHeight();
548 float factor = (mActualHeight - allChildrenVisibleHeight)
549 / (float) (maxContentHeight - allChildrenVisibleHeight);
550 return Math.max(0.0f, Math.min(1.0f, factor));
551 }
552
553 private int getChildrenExpandStartHeight() {
554 int intrinsicHeight = mNotificationHeaderHeight;
555 int visibleChildren = 0;
556 int childCount = mChildren.size();
557 for (int i = 0; i < childCount; i++) {
558 if (visibleChildren >= NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED) {
559 break;
560 }
561 ExpandableNotificationRow child = mChildren.get(i);
562 intrinsicHeight += child.getMinHeight();
563 visibleChildren++;
564 }
565 if (visibleChildren > 0) {
566 intrinsicHeight += (visibleChildren - 1) * mChildPadding;
567 }
568 intrinsicHeight += mCollapsedBottompadding;
569 return intrinsicHeight;
Selim Cinek83bc7832015-10-22 13:26:54 -0700570 }
571
572 public int getMinHeight() {
Selim Cinekb55386d2015-12-16 17:26:49 -0800573 return getIntrinsicHeight(NUMBER_OF_CHILDREN_WHEN_COLLAPSED);
574 }
575
Selim Cinek2c584612016-02-29 16:14:25 -0800576 public int getMinExpandHeight(boolean onKeyguard) {
577 int maxAllowedVisibleChildren = onKeyguard ? NUMBER_OF_CHILDREN_WHEN_COLLAPSED
578 : getMaxAllowedVisibleChildren(true /* forceCollapsed */);
579 int minExpandHeight = mNotificationHeaderHeight;
580 int visibleChildren = 0;
581 boolean firstChild = true;
582 int childCount = mChildren.size();
583 for (int i = 0; i < childCount; i++) {
584 if (visibleChildren >= maxAllowedVisibleChildren) {
585 break;
586 }
587 if (!firstChild) {
588 minExpandHeight += mChildPadding;
589 } else {
590 firstChild = false;
591 }
592 ExpandableNotificationRow child = mChildren.get(i);
593 minExpandHeight += child.getMinHeight();
594 visibleChildren++;
595 }
596 minExpandHeight += mCollapsedBottompadding;
597 return minExpandHeight;
Selim Cinekb5605e52015-02-20 18:21:41 +0100598 }
Selim Cinek9c7712d2015-12-08 19:19:48 -0800599
600 public void setDark(boolean dark, boolean fade, long delay) {
601 if (mGroupOverflowContainer != null) {
602 mOverflowInvertHelper.setInverted(dark, fade, delay);
603 }
604 }
Selim Cinek01af3342016-02-09 19:25:31 -0800605
606 public void reInflateViews() {
607 initDimens();
608 for (int i = 0; i < mDividers.size(); i++) {
609 View prevDivider = mDividers.get(i);
610 int index = indexOfChild(prevDivider);
611 removeView(prevDivider);
612 View divider = inflateDivider();
613 addView(divider, index);
614 mDividers.set(i, divider);
615 }
616 }
Selim Cinek42357e02016-02-24 18:48:01 -0800617
618 public void setUserLocked(boolean userLocked) {
619 mUserLocked = userLocked;
620 int childCount = mChildren.size();
621 for (int i = 0; i < childCount; i++) {
622 ExpandableNotificationRow child = mChildren.get(i);
623 child.setUserLocked(userLocked);
624 }
625 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100626}