blob: 60061c6a9ad2a3c8cff2ef29c26c2790ee5174f9 [file] [log] [blame]
Selim Cinek67b22602014-03-10 15:40:16 +01001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
Rohan Shah20790b82018-07-02 17:21:04 -070017package com.android.systemui.statusbar.notification.stack;
Selim Cinek67b22602014-03-10 15:40:16 +010018
19import android.content.Context;
Anthony Chen9fe1ee72017-04-07 13:53:37 -070020import android.content.res.Resources;
Christoph Studer6e3eceb2014-04-01 18:40:27 +020021import android.util.Log;
Selim Cinek67b22602014-03-10 15:40:16 +010022import android.view.View;
23import android.view.ViewGroup;
Gus Prevase2d6f042018-10-17 15:25:30 -040024
Selim Cinek67b22602014-03-10 15:40:16 +010025import com.android.systemui.R;
Selim Cinekcde90e52016-12-22 21:01:49 +010026import com.android.systemui.statusbar.EmptyShadeView;
Gus Prevase2d6f042018-10-17 15:25:30 -040027import com.android.systemui.statusbar.NotificationShelf;
28import com.android.systemui.statusbar.notification.NotificationUtils;
Rohan Shah20790b82018-07-02 17:21:04 -070029import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
30import com.android.systemui.statusbar.notification.row.ExpandableView;
31import com.android.systemui.statusbar.notification.row.FooterView;
Selim Cinek67b22602014-03-10 15:40:16 +010032
Jorim Jaggid4a57442014-04-10 02:45:55 +020033import java.util.ArrayList;
Selim Cinek42357e02016-02-24 18:48:01 -080034import java.util.HashMap;
Selim Cinekb5605e52015-02-20 18:21:41 +010035import java.util.List;
Jorim Jaggid4a57442014-04-10 02:45:55 +020036
Selim Cinek67b22602014-03-10 15:40:16 +010037/**
Rohan Shah20790b82018-07-02 17:21:04 -070038 * The Algorithm of the {@link com.android.systemui.statusbar.notification.stack
Selim Cinek67b22602014-03-10 15:40:16 +010039 * .NotificationStackScrollLayout} which can be queried for {@link com.android.systemui.statusbar
40 * .stack.StackScrollState}
41 */
42public class StackScrollAlgorithm {
43
Gus Prevas0fa58d62019-01-11 13:58:40 -050044 static final boolean ANCHOR_SCROLLING = false;
45
Christoph Studer6e3eceb2014-04-01 18:40:27 +020046 private static final String LOG_TAG = "StackScrollAlgorithm";
Dave Mankoffa4d195d2018-11-16 13:33:27 -050047 private final ViewGroup mHostView;
Christoph Studer6e3eceb2014-04-01 18:40:27 +020048
Selim Cinek67b22602014-03-10 15:40:16 +010049 private int mPaddingBetweenElements;
Selim Cinek61633a82016-01-25 15:54:10 -080050 private int mIncreasedPaddingBetweenElements;
Gus Prevase2d6f042018-10-17 15:25:30 -040051 private int mGapHeight;
Selim Cinek67b22602014-03-10 15:40:16 +010052 private int mCollapsedSize;
Selim Cinek67b22602014-03-10 15:40:16 +010053
Selim Cinek67b22602014-03-10 15:40:16 +010054 private StackScrollAlgorithmState mTempAlgorithmState = new StackScrollAlgorithmState();
Selim Cinek1685e632014-04-08 02:27:49 +020055 private boolean mIsExpanded;
Anthony Chen9fe1ee72017-04-07 13:53:37 -070056 private boolean mClipNotificationScrollToTop;
Selim Cinekcafa87f2016-10-26 17:00:17 -070057 private int mStatusBarHeight;
Selim Cinekaa9db1f2018-02-27 17:35:47 -080058 private float mHeadsUpInset;
Selim Cinek99e9adf2018-03-15 09:17:47 -070059 private int mPinnedZTranslationExtra;
Selim Cinek67b22602014-03-10 15:40:16 +010060
Ned Burns9eb06332019-04-23 16:02:12 -040061 public StackScrollAlgorithm(
62 Context context,
63 ViewGroup hostView) {
Dave Mankoffa4d195d2018-11-16 13:33:27 -050064 mHostView = hostView;
Selim Cinekaf0dc312015-12-15 17:01:44 -080065 initView(context);
Selim Cinek34c0a8d2014-05-12 00:01:43 +020066 }
67
Selim Cinekaf0dc312015-12-15 17:01:44 -080068 public void initView(Context context) {
69 initConstants(context);
Selim Cinek34c0a8d2014-05-12 00:01:43 +020070 }
71
Selim Cinek67b22602014-03-10 15:40:16 +010072 private void initConstants(Context context) {
Anthony Chen9fe1ee72017-04-07 13:53:37 -070073 Resources res = context.getResources();
74 mPaddingBetweenElements = res.getDimensionPixelSize(
Selim Cinekdb167372016-11-17 15:41:17 -080075 R.dimen.notification_divider_height);
Anthony Chen9fe1ee72017-04-07 13:53:37 -070076 mIncreasedPaddingBetweenElements =
77 res.getDimensionPixelSize(R.dimen.notification_divider_height_increased);
78 mCollapsedSize = res.getDimensionPixelSize(R.dimen.notification_min_height);
79 mStatusBarHeight = res.getDimensionPixelSize(R.dimen.status_bar_height);
80 mClipNotificationScrollToTop = res.getBoolean(R.bool.config_clipNotificationScrollToTop);
Selim Cinekaa9db1f2018-02-27 17:35:47 -080081 mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize(
82 R.dimen.heads_up_status_bar_padding);
Selim Cinek99e9adf2018-03-15 09:17:47 -070083 mPinnedZTranslationExtra = res.getDimensionPixelSize(
84 R.dimen.heads_up_pinned_elevation);
Gus Prevase2d6f042018-10-17 15:25:30 -040085 mGapHeight = res.getDimensionPixelSize(R.dimen.notification_section_divider_height);
Jorim Jaggid7c1fae2014-08-13 18:27:47 +020086 }
Selim Cinek67b22602014-03-10 15:40:16 +010087
Dave Mankoffa4d195d2018-11-16 13:33:27 -050088 /**
89 * Updates the state of all children in the hostview based on this algorithm.
90 */
91 public void resetViewStates(AmbientState ambientState) {
Selim Cinek67b22602014-03-10 15:40:16 +010092 // The state of the local variables are saved in an algorithmState to easily subdivide it
93 // into multiple phases.
94 StackScrollAlgorithmState algorithmState = mTempAlgorithmState;
95
96 // First we reset the view states to their default values.
Dave Mankoffa4d195d2018-11-16 13:33:27 -050097 resetChildViewStates();
Selim Cinek67b22602014-03-10 15:40:16 +010098
Dave Mankoffa4d195d2018-11-16 13:33:27 -050099 initAlgorithmState(mHostView, algorithmState, ambientState);
Selim Cinek1408eb52014-06-02 14:45:38 +0200100
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500101 updatePositionsForState(algorithmState, ambientState);
Selim Cinek67b22602014-03-10 15:40:16 +0100102
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500103 updateZValuesForState(algorithmState, ambientState);
Selim Cinek3776fe02016-02-04 13:32:43 -0800104
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500105 updateHeadsUpStates(algorithmState, ambientState);
Selim Cinek5040f2e2019-02-14 18:22:42 -0800106 updatePulsingStates(algorithmState, ambientState);
Selim Cinekeb973562014-05-02 17:07:49 +0200107
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500108 updateDimmedActivatedHideSensitive(ambientState, algorithmState);
109 updateClipping(algorithmState, ambientState);
110 updateSpeedBumpState(algorithmState, ambientState);
111 updateShelfState(ambientState);
112 getNotificationChildrenStates(algorithmState, ambientState);
Selim Cinekb5605e52015-02-20 18:21:41 +0100113 }
114
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500115 private void resetChildViewStates() {
116 int numChildren = mHostView.getChildCount();
117 for (int i = 0; i < numChildren; i++) {
118 ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
119 child.resetViewState();
120 }
121 }
122
123 private void getNotificationChildrenStates(StackScrollAlgorithmState algorithmState,
Selim Cinekc25989e2018-02-16 16:42:14 -0800124 AmbientState ambientState) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100125 int childCount = algorithmState.visibleChildren.size();
126 for (int i = 0; i < childCount; i++) {
127 ExpandableView v = algorithmState.visibleChildren.get(i);
128 if (v instanceof ExpandableNotificationRow) {
129 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500130 row.updateChildrenStates(ambientState);
Selim Cinekb5605e52015-02-20 18:21:41 +0100131 }
132 }
Selim Cinek3d2b94bf2014-07-02 22:12:47 +0200133 }
134
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500135 private void updateSpeedBumpState(StackScrollAlgorithmState algorithmState,
136 AmbientState ambientState) {
Selim Cinek3d2b94bf2014-07-02 22:12:47 +0200137 int childCount = algorithmState.visibleChildren.size();
Selim Cinekdb167372016-11-17 15:41:17 -0800138 int belowSpeedBump = ambientState.getSpeedBumpIndex();
Selim Cinek3d2b94bf2014-07-02 22:12:47 +0200139 for (int i = 0; i < childCount; i++) {
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500140 ExpandableView child = algorithmState.visibleChildren.get(i);
141 ExpandableViewState childViewState = child.getViewState();
Selim Cinek3107cfa2014-07-22 15:24:29 +0200142
143 // The speed bump can also be gone, so equality needs to be taken when comparing
144 // indices.
Selim Cinekdb167372016-11-17 15:41:17 -0800145 childViewState.belowSpeedBump = i >= belowSpeedBump;
Selim Cinek3d2b94bf2014-07-02 22:12:47 +0200146 }
Selim Cinekdb167372016-11-17 15:41:17 -0800147
148 }
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500149
150 private void updateShelfState(AmbientState ambientState) {
Selim Cinek281c2022016-10-13 19:14:43 -0700151 NotificationShelf shelf = ambientState.getShelf();
Eliot Courtney5d3d2d02018-01-18 15:59:03 +0900152 if (shelf != null) {
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500153 shelf.updateState(ambientState);
Eliot Courtney5d3d2d02018-01-18 15:59:03 +0900154 }
Selim Cinekf54090e2014-06-17 17:24:51 -0700155 }
156
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500157 private void updateClipping(StackScrollAlgorithmState algorithmState,
158 AmbientState ambientState) {
Selim Cinek355652a2016-12-07 13:32:12 -0800159 float drawStart = !ambientState.isOnKeyguard() ? ambientState.getTopPadding()
Selim Cinek2627d722018-01-19 12:16:49 -0800160 + ambientState.getStackTranslation() + ambientState.getExpandAnimationTopChange()
161 : 0;
Selim Cinek708a6c12014-05-28 14:16:02 +0200162 float previousNotificationEnd = 0;
163 float previousNotificationStart = 0;
Selim Cinek708a6c12014-05-28 14:16:02 +0200164 int childCount = algorithmState.visibleChildren.size();
165 for (int i = 0; i < childCount; i++) {
166 ExpandableView child = algorithmState.visibleChildren.get(i);
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500167 ExpandableViewState state = child.getViewState();
Selim Cinek9b9d6e12017-11-30 12:29:47 +0100168 if (!child.mustStayOnScreen() || state.headsUpIsVisible) {
Selim Cinek3776fe02016-02-04 13:32:43 -0800169 previousNotificationEnd = Math.max(drawStart, previousNotificationEnd);
170 previousNotificationStart = Math.max(drawStart, previousNotificationStart);
171 }
Selim Cinek587cbf32016-01-19 11:36:18 -0800172 float newYTranslation = state.yTranslation;
173 float newHeight = state.height;
Selim Cinek708a6c12014-05-28 14:16:02 +0200174 float newNotificationEnd = newYTranslation + newHeight;
Mady Mellor53ac1ef2016-06-20 13:11:38 -0700175 boolean isHeadsUp = (child instanceof ExpandableNotificationRow)
176 && ((ExpandableNotificationRow) child).isPinned();
Anthony Chen9fe1ee72017-04-07 13:53:37 -0700177 if (mClipNotificationScrollToTop
178 && !state.inShelf && newYTranslation < previousNotificationEnd
Jorim Jaggi0fdf5742016-06-27 11:50:58 -0700179 && (!isHeadsUp || ambientState.isShadeExpanded())) {
Mady Mellorc128f222016-04-26 11:42:46 -0700180 // The previous view is overlapping on top, clip!
181 float overlapAmount = previousNotificationEnd - newYTranslation;
182 state.clipTopAmount = (int) overlapAmount;
Selim Cinekc5baa3e2014-10-29 19:04:19 +0100183 } else {
Mady Mellorc128f222016-04-26 11:42:46 -0700184 state.clipTopAmount = 0;
Selim Cinek9c17b772015-07-07 20:37:09 -0700185 }
186
Selim Cinek708a6c12014-05-28 14:16:02 +0200187 if (!child.isTransparent()) {
188 // Only update the previous values if we are not transparent,
189 // otherwise we would clip to a transparent view.
Mady Mellorc128f222016-04-26 11:42:46 -0700190 previousNotificationEnd = newNotificationEnd;
191 previousNotificationStart = newYTranslation;
Selim Cinek708a6c12014-05-28 14:16:02 +0200192 }
193 }
194 }
195
Selim Cinek9c17b772015-07-07 20:37:09 -0700196 public static boolean canChildBeDismissed(View v) {
Selim Cinek9e624e72016-07-20 13:46:49 -0700197 if (!(v instanceof ExpandableNotificationRow)) {
198 return false;
Selim Cinek38d429f2016-06-03 11:46:56 -0700199 }
Selim Cinek9e624e72016-07-20 13:46:49 -0700200 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
Nadia Benbernou7a18c812019-02-08 16:23:10 -0500201 if (row.isBlockingHelperShowingAndTranslationFinished()) {
202 return true;
203 }
Aaron Heuckroth1dd67cb2018-06-14 14:28:08 -0400204 if (row.areGutsExposed() || !row.getEntry().hasFinishedInitialization()) {
Selim Cinek9e624e72016-07-20 13:46:49 -0700205 return false;
206 }
207 return row.canViewBeDismissed();
Selim Cinek9c17b772015-07-07 20:37:09 -0700208 }
209
Selim Cinek708a6c12014-05-28 14:16:02 +0200210 /**
Jorim Jaggiae441282014-08-01 02:45:18 +0200211 * Updates the dimmed, activated and hiding sensitive states of the children.
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200212 */
Jorim Jaggiae441282014-08-01 02:45:18 +0200213 private void updateDimmedActivatedHideSensitive(AmbientState ambientState,
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500214 StackScrollAlgorithmState algorithmState) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200215 boolean dimmed = ambientState.isDimmed();
Lucas Dupin16cfe452018-02-08 13:14:50 -0800216 boolean dark = ambientState.isFullyDark();
Jorim Jaggiae441282014-08-01 02:45:18 +0200217 boolean hideSensitive = ambientState.isHideSensitive();
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200218 View activatedChild = ambientState.getActivatedChild();
219 int childCount = algorithmState.visibleChildren.size();
220 for (int i = 0; i < childCount; i++) {
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500221 ExpandableView child = algorithmState.visibleChildren.get(i);
222 ExpandableViewState childViewState = child.getViewState();
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200223 childViewState.dimmed = dimmed;
John Spurlockbf370992014-06-17 13:58:31 -0400224 childViewState.dark = dark;
Jorim Jaggiae441282014-08-01 02:45:18 +0200225 childViewState.hideSensitive = hideSensitive;
Selim Cinekb89de4e2014-06-10 10:47:05 +0200226 boolean isActivatedChild = activatedChild == child;
Jorim Jaggi4538cee2014-09-09 15:21:38 +0200227 if (dimmed && isActivatedChild) {
Selim Cinek281c2022016-10-13 19:14:43 -0700228 childViewState.zTranslation += 2.0f * ambientState.getZDistanceBetweenElements();
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200229 }
230 }
Selim Cinekeb973562014-05-02 17:07:49 +0200231 }
232
233 /**
Selim Cinek61633a82016-01-25 15:54:10 -0800234 * Initialize the algorithm state like updating the visible children.
Jorim Jaggid4a57442014-04-10 02:45:55 +0200235 */
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500236 private void initAlgorithmState(ViewGroup hostView, StackScrollAlgorithmState state,
Selim Cinek3776fe02016-02-04 13:32:43 -0800237 AmbientState ambientState) {
Selim Cinek3776fe02016-02-04 13:32:43 -0800238 float bottomOverScroll = ambientState.getOverScrollAmount(false /* onTop */);
239
240 int scrollY = ambientState.getScrollY();
241
242 // Due to the overScroller, the stackscroller can have negative scroll state. This is
243 // already accounted for by the top padding and doesn't need an additional adaption
244 scrollY = Math.max(0, scrollY);
245 state.scrollY = (int) (scrollY + bottomOverScroll);
246
Gus Prevas0fa58d62019-01-11 13:58:40 -0500247 if (ANCHOR_SCROLLING) {
248 state.anchorViewY = (int) (ambientState.getAnchorViewY() - bottomOverScroll);
249 }
250
Selim Cinek3776fe02016-02-04 13:32:43 -0800251 //now init the visible children and update paddings
Jorim Jaggid4a57442014-04-10 02:45:55 +0200252 int childCount = hostView.getChildCount();
253 state.visibleChildren.clear();
254 state.visibleChildren.ensureCapacity(childCount);
Selim Cineka7ed2c12017-01-23 20:47:24 -0800255 state.paddingMap.clear();
Selim Cinekb036ca42015-02-20 15:56:28 +0100256 int notGoneIndex = 0;
Selim Cinek61633a82016-01-25 15:54:10 -0800257 ExpandableView lastView = null;
Selim Cinekd96ed402017-07-28 18:19:03 -0700258 int firstHiddenIndex = ambientState.isDark()
259 ? (ambientState.hasPulsingNotifications() ? 1 : 0)
260 : childCount;
261
262 // The goal here is to fill the padding map, by iterating over how much padding each child
263 // needs. The map is thereby reused, by first filling it with the padding amount and when
264 // iterating over it again, it's filled with the actual resolved value.
265
Jorim Jaggid4a57442014-04-10 02:45:55 +0200266 for (int i = 0; i < childCount; i++) {
Gus Prevas0fa58d62019-01-11 13:58:40 -0500267 if (ANCHOR_SCROLLING) {
268 if (i == ambientState.getAnchorViewIndex()) {
269 state.anchorViewIndex = state.visibleChildren.size();
270 }
271 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200272 ExpandableView v = (ExpandableView) hostView.getChildAt(i);
Jorim Jaggid4a57442014-04-10 02:45:55 +0200273 if (v.getVisibility() != View.GONE) {
Selim Cinek281c2022016-10-13 19:14:43 -0700274 if (v == ambientState.getShelf()) {
275 continue;
276 }
Selim Cinekd96ed402017-07-28 18:19:03 -0700277 if (i >= firstHiddenIndex) {
278 // we need normal padding now, to be in sync with what the stack calculates
279 lastView = null;
Selim Cinekd96ed402017-07-28 18:19:03 -0700280 }
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500281 notGoneIndex = updateNotGoneIndex(state, notGoneIndex, v);
Selim Cinekd96ed402017-07-28 18:19:03 -0700282 float increasedPadding = v.getIncreasedPaddingAmount();
Selim Cinek42357e02016-02-24 18:48:01 -0800283 if (increasedPadding != 0.0f) {
Selim Cineka7ed2c12017-01-23 20:47:24 -0800284 state.paddingMap.put(v, increasedPadding);
Selim Cinek61633a82016-01-25 15:54:10 -0800285 if (lastView != null) {
Selim Cineka7ed2c12017-01-23 20:47:24 -0800286 Float prevValue = state.paddingMap.get(lastView);
287 float newValue = getPaddingForValue(increasedPadding);
288 if (prevValue != null) {
289 float prevPadding = getPaddingForValue(prevValue);
290 if (increasedPadding > 0) {
291 newValue = NotificationUtils.interpolate(
292 prevPadding,
293 newValue,
294 increasedPadding);
295 } else if (prevValue > 0) {
296 newValue = NotificationUtils.interpolate(
297 newValue,
298 prevPadding,
299 prevValue);
300 }
301 }
302 state.paddingMap.put(lastView, newValue);
Selim Cinek61633a82016-01-25 15:54:10 -0800303 }
Selim Cineka7ed2c12017-01-23 20:47:24 -0800304 } else if (lastView != null) {
Selim Cinekd96ed402017-07-28 18:19:03 -0700305
306 // Let's now resolve the value to an actual padding
Selim Cineka7ed2c12017-01-23 20:47:24 -0800307 float newValue = getPaddingForValue(state.paddingMap.get(lastView));
308 state.paddingMap.put(lastView, newValue);
Selim Cinek61633a82016-01-25 15:54:10 -0800309 }
Selim Cinekb5605e52015-02-20 18:21:41 +0100310 if (v instanceof ExpandableNotificationRow) {
311 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700312
313 // handle the notgoneIndex for the children as well
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500314 List<ExpandableNotificationRow> children = row.getNotificationChildren();
Selim Cinek83bc7832015-10-22 13:26:54 -0700315 if (row.isSummaryWithChildren() && children != null) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100316 for (ExpandableNotificationRow childRow : children) {
317 if (childRow.getVisibility() != View.GONE) {
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500318 ExpandableViewState childState = childRow.getViewState();
Selim Cinekb5605e52015-02-20 18:21:41 +0100319 childState.notGoneIndex = notGoneIndex;
320 notGoneIndex++;
321 }
322 }
323 }
324 }
Selim Cinek61633a82016-01-25 15:54:10 -0800325 lastView = v;
Jorim Jaggid4a57442014-04-10 02:45:55 +0200326 }
327 }
Selim Cinek2627d722018-01-19 12:16:49 -0800328 ExpandableNotificationRow expandingNotification = ambientState.getExpandingNotification();
329 state.indexOfExpandingNotification = expandingNotification != null
Selim Cinekc25989e2018-02-16 16:42:14 -0800330 ? expandingNotification.isChildInGroup()
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500331 ? state.visibleChildren.indexOf(expandingNotification.getNotificationParent())
332 : state.visibleChildren.indexOf(expandingNotification)
Selim Cinek2627d722018-01-19 12:16:49 -0800333 : -1;
Jorim Jaggid4a57442014-04-10 02:45:55 +0200334 }
335
Selim Cineka7ed2c12017-01-23 20:47:24 -0800336 private float getPaddingForValue(Float increasedPadding) {
337 if (increasedPadding == null) {
338 return mPaddingBetweenElements;
339 } else if (increasedPadding >= 0.0f) {
340 return NotificationUtils.interpolate(
341 mPaddingBetweenElements,
342 mIncreasedPaddingBetweenElements,
343 increasedPadding);
344 } else {
345 return NotificationUtils.interpolate(
346 0,
347 mPaddingBetweenElements,
348 1.0f + increasedPadding);
349 }
350 }
351
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500352 private int updateNotGoneIndex(StackScrollAlgorithmState state, int notGoneIndex,
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700353 ExpandableView v) {
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500354 ExpandableViewState viewState = v.getViewState();
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700355 viewState.notGoneIndex = notGoneIndex;
356 state.visibleChildren.add(v);
357 notGoneIndex++;
358 return notGoneIndex;
359 }
360
Jorim Jaggid4a57442014-04-10 02:45:55 +0200361 /**
Selim Cinek67b22602014-03-10 15:40:16 +0100362 * Determine the positions for the views. This is the main part of the algorithm.
363 *
Selim Cinek67b22602014-03-10 15:40:16 +0100364 * @param algorithmState The state in which the current pass of the algorithm is currently in
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500365 * @param ambientState The current ambient state
Selim Cinek67b22602014-03-10 15:40:16 +0100366 */
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500367 private void updatePositionsForState(StackScrollAlgorithmState algorithmState,
368 AmbientState ambientState) {
Gus Prevas0fa58d62019-01-11 13:58:40 -0500369 if (ANCHOR_SCROLLING) {
370 float currentYPosition = algorithmState.anchorViewY;
371 int childCount = algorithmState.visibleChildren.size();
372 for (int i = algorithmState.anchorViewIndex; i < childCount; i++) {
Gus Prevas0fa58d62019-01-11 13:58:40 -0500373 currentYPosition = updateChild(i, algorithmState, ambientState, currentYPosition,
374 false /* reverse */);
375 }
376 currentYPosition = algorithmState.anchorViewY;
377 for (int i = algorithmState.anchorViewIndex - 1; i >= 0; i--) {
Gus Prevas0fa58d62019-01-11 13:58:40 -0500378 currentYPosition = updateChild(i, algorithmState, ambientState, currentYPosition,
379 true /* reverse */);
380 }
381 } else {
382 // The y coordinate of the current child.
383 float currentYPosition = -algorithmState.scrollY;
384 int childCount = algorithmState.visibleChildren.size();
385 for (int i = 0; i < childCount; i++) {
Gus Prevas0fa58d62019-01-11 13:58:40 -0500386 currentYPosition = updateChild(i, algorithmState, ambientState, currentYPosition,
387 false /* reverse */);
388 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700389 }
390 }
391
Gus Prevas0fa58d62019-01-11 13:58:40 -0500392 /**
393 * Populates the {@link ExpandableViewState} for a single child.
394 *
395 * @param i The index of the child in
396 * {@link StackScrollAlgorithmState#visibleChildren}.
397 * @param algorithmState The overall output state of the algorithm.
398 * @param ambientState The input state provided to the algorithm.
399 * @param currentYPosition The Y position of the current pass of the algorithm. For a forward
400 * pass, this should be the top of the child; for a reverse pass, the
401 * bottom of the child.
402 * @param reverse Whether we're laying out children in the reverse direction (Y
403 * positions
404 * decreasing) instead of the forward direction (Y positions
405 * increasing).
406 * @return The Y position after laying out the child. This will be the {@code currentYPosition}
407 * for the next call to this method, after adjusting for any gaps between children.
408 */
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500409 protected float updateChild(
410 int i,
411 StackScrollAlgorithmState algorithmState,
412 AmbientState ambientState,
Gus Prevas0fa58d62019-01-11 13:58:40 -0500413 float currentYPosition,
414 boolean reverse) {
Muyuan Li87798022016-04-07 17:51:25 -0700415 ExpandableView child = algorithmState.visibleChildren.get(i);
Ned Burns9eb06332019-04-23 16:02:12 -0400416 final boolean applyGapHeight =
417 childNeedsGapHeight(ambientState.getSectionProvider(), algorithmState, i, child);
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500418 ExpandableViewState childViewState = child.getViewState();
Selim Cinekbbcebde2016-11-09 18:28:20 -0800419 childViewState.location = ExpandableViewState.LOCATION_UNKNOWN;
Ned Burns9eb06332019-04-23 16:02:12 -0400420
421 if (applyGapHeight && !reverse) {
422 currentYPosition += mGapHeight;
423 }
424
Muyuan Li87798022016-04-07 17:51:25 -0700425 int paddingAfterChild = getPaddingAfterChild(algorithmState, child);
426 int childHeight = getMaxAllowedChildHeight(child);
Gus Prevas0fa58d62019-01-11 13:58:40 -0500427 if (reverse) {
428 childViewState.yTranslation = currentYPosition - (childHeight + paddingAfterChild);
429 if (currentYPosition <= 0) {
430 childViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP;
431 }
432 } else {
433 childViewState.yTranslation = currentYPosition;
Gus Prevase2d6f042018-10-17 15:25:30 -0400434 }
Julia Reynoldsed1c9af2018-03-21 15:21:09 -0400435 boolean isFooterView = child instanceof FooterView;
Selim Cinekcde90e52016-12-22 21:01:49 +0100436 boolean isEmptyShadeView = child instanceof EmptyShadeView;
Muyuan Li87798022016-04-07 17:51:25 -0700437
Selim Cinekdb167372016-11-17 15:41:17 -0800438 childViewState.location = ExpandableViewState.LOCATION_MAIN_AREA;
Selim Cinek9b9d6e12017-11-30 12:29:47 +0100439 float inset = ambientState.getTopPadding() + ambientState.getStackTranslation();
Selim Cinekc25989e2018-02-16 16:42:14 -0800440 if (i <= algorithmState.getIndexOfExpandingNotification()) {
Selim Cinek2627d722018-01-19 12:16:49 -0800441 inset += ambientState.getExpandAnimationTopChange();
442 }
Selim Cinek9b9d6e12017-11-30 12:29:47 +0100443 if (child.mustStayOnScreen() && childViewState.yTranslation >= 0) {
444 // Even if we're not scrolled away we're in view and we're also not in the
445 // shelf. We can relax the constraints and let us scroll off the top!
446 float end = childViewState.yTranslation + childViewState.height + inset;
447 childViewState.headsUpIsVisible = end < ambientState.getMaxHeadsUpTranslation();
448 }
Julia Reynoldsed1c9af2018-03-21 15:21:09 -0400449 if (isFooterView) {
Selim Cinekdb167372016-11-17 15:41:17 -0800450 childViewState.yTranslation = Math.min(childViewState.yTranslation,
451 ambientState.getInnerHeight() - childHeight);
Selim Cinekcde90e52016-12-22 21:01:49 +0100452 } else if (isEmptyShadeView) {
453 childViewState.yTranslation = ambientState.getInnerHeight() - childHeight
454 + ambientState.getStackTranslation() * 0.25f;
Muyuan Li87798022016-04-07 17:51:25 -0700455 } else {
Selim Cinek2627d722018-01-19 12:16:49 -0800456 clampPositionToShelf(child, childViewState, ambientState);
Muyuan Li87798022016-04-07 17:51:25 -0700457 }
458
Gus Prevas0fa58d62019-01-11 13:58:40 -0500459 if (reverse) {
460 currentYPosition = childViewState.yTranslation;
Ned Burns9eb06332019-04-23 16:02:12 -0400461 if (applyGapHeight) {
462 currentYPosition -= mGapHeight;
463 }
Gus Prevas0fa58d62019-01-11 13:58:40 -0500464 } else {
465 currentYPosition = childViewState.yTranslation + childHeight + paddingAfterChild;
466 if (currentYPosition <= 0) {
467 childViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP;
468 }
Muyuan Li87798022016-04-07 17:51:25 -0700469 }
Selim Cinekbbcebde2016-11-09 18:28:20 -0800470 if (childViewState.location == ExpandableViewState.LOCATION_UNKNOWN) {
Muyuan Li87798022016-04-07 17:51:25 -0700471 Log.wtf(LOG_TAG, "Failed to assign location for child " + i);
472 }
473
Selim Cinek9b9d6e12017-11-30 12:29:47 +0100474 childViewState.yTranslation += inset;
Muyuan Li87798022016-04-07 17:51:25 -0700475 return currentYPosition;
476 }
477
Ned Burns9eb06332019-04-23 16:02:12 -0400478 private boolean childNeedsGapHeight(
479 SectionProvider sectionProvider,
480 StackScrollAlgorithmState algorithmState,
481 int visibleIndex,
482 View child) {
483 boolean needsGapHeight = sectionProvider.beginsSection(child) && visibleIndex > 0;
484 if (ANCHOR_SCROLLING) {
485 needsGapHeight &= visibleIndex != algorithmState.anchorViewIndex;
486 }
487 return needsGapHeight;
488 }
489
Muyuan Li87798022016-04-07 17:51:25 -0700490 protected int getPaddingAfterChild(StackScrollAlgorithmState algorithmState,
Selim Cinek61633a82016-01-25 15:54:10 -0800491 ExpandableView child) {
Selim Cinek281c2022016-10-13 19:14:43 -0700492 return algorithmState.getPaddingAfterChild(child);
Selim Cinek61633a82016-01-25 15:54:10 -0800493 }
494
Selim Cinek5040f2e2019-02-14 18:22:42 -0800495 private void updatePulsingStates(StackScrollAlgorithmState algorithmState,
496 AmbientState ambientState) {
497 int childCount = algorithmState.visibleChildren.size();
498 for (int i = 0; i < childCount; i++) {
499 View child = algorithmState.visibleChildren.get(i);
500 if (!(child instanceof ExpandableNotificationRow)) {
501 continue;
502 }
503 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
Selim Cinek459aee32019-02-20 11:18:56 -0800504 if (!row.showingAmbientPulsing() || (i == 0 && ambientState.isPulseExpanding())) {
Selim Cinek5040f2e2019-02-14 18:22:42 -0800505 continue;
506 }
507 ExpandableViewState viewState = row.getViewState();
508 viewState.hidden = false;
509 }
510 }
511
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500512 private void updateHeadsUpStates(StackScrollAlgorithmState algorithmState,
513 AmbientState ambientState) {
Selim Cineka4baaa32015-04-20 14:27:54 -0700514 int childCount = algorithmState.visibleChildren.size();
515 ExpandableNotificationRow topHeadsUpEntry = null;
516 for (int i = 0; i < childCount; i++) {
517 View child = algorithmState.visibleChildren.get(i);
518 if (!(child instanceof ExpandableNotificationRow)) {
519 break;
520 }
521 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
522 if (!row.isHeadsUp()) {
523 break;
Selim Cineka4baaa32015-04-20 14:27:54 -0700524 }
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500525 ExpandableViewState childState = row.getViewState();
Selim Cinek9b9d6e12017-11-30 12:29:47 +0100526 if (topHeadsUpEntry == null && row.mustStayOnScreen() && !childState.headsUpIsVisible) {
Selim Cinek3776fe02016-02-04 13:32:43 -0800527 topHeadsUpEntry = row;
Selim Cinekbbcebde2016-11-09 18:28:20 -0800528 childState.location = ExpandableViewState.LOCATION_FIRST_HUN;
Selim Cinek3776fe02016-02-04 13:32:43 -0800529 }
Selim Cinek1f3f5442015-04-10 17:54:46 -0700530 boolean isTopEntry = topHeadsUpEntry == row;
Selim Cinek3776fe02016-02-04 13:32:43 -0800531 float unmodifiedEndLocation = childState.yTranslation + childState.height;
Selim Cinek131c1e22015-05-11 19:04:49 -0700532 if (mIsExpanded) {
Selim Cinek9b9d6e12017-11-30 12:29:47 +0100533 if (row.mustStayOnScreen() && !childState.headsUpIsVisible) {
534 // Ensure that the heads up is always visible even when scrolled off
535 clampHunToTop(ambientState, row, childState);
Selim Cinek459aee32019-02-20 11:18:56 -0800536 if (i == 0 && row.isAboveShelf()) {
Selim Cinek9b9d6e12017-11-30 12:29:47 +0100537 // the first hun can't get off screen.
538 clampHunToMaxTranslation(ambientState, row, childState);
539 childState.hidden = false;
540 }
Selim Cinekd127d792016-11-01 19:11:41 -0700541 }
Selim Cinek131c1e22015-05-11 19:04:49 -0700542 }
Selim Cinek684a4422015-04-15 16:18:39 -0700543 if (row.isPinned()) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800544 childState.yTranslation = Math.max(childState.yTranslation, mHeadsUpInset);
Selim Cinek31aada42015-12-18 17:51:15 -0800545 childState.height = Math.max(row.getIntrinsicHeight(), childState.height);
Selim Cinekcafa87f2016-10-26 17:00:17 -0700546 childState.hidden = false;
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500547 ExpandableViewState topState =
548 topHeadsUpEntry == null ? null : topHeadsUpEntry.getViewState();
Selim Cinek61cfd4b2017-12-08 12:42:36 -0800549 if (topState != null && !isTopEntry && (!mIsExpanded
Selim Cinek3776fe02016-02-04 13:32:43 -0800550 || unmodifiedEndLocation < topState.yTranslation + topState.height)) {
Selim Cinek684a4422015-04-15 16:18:39 -0700551 // Ensure that a headsUp doesn't vertically extend further than the heads-up at
552 // the top most z-position
Selim Cinek31aada42015-12-18 17:51:15 -0800553 childState.height = row.getIntrinsicHeight();
Selim Cineke53e6bb2015-04-13 16:14:26 -0700554 childState.yTranslation = topState.yTranslation + topState.height
555 - childState.height;
Selim Cinek1f3f5442015-04-10 17:54:46 -0700556 }
felkachang529bfe62018-07-04 12:51:44 +0800557
558 // heads up notification show and this row is the top entry of heads up
559 // notifications. i.e. this row should be the only one row that has input field
560 // To check if the row need to do translation according to scroll Y
561 // heads up show full of row's content and any scroll y indicate that the
562 // translationY need to move up the HUN.
Gus Prevas0fa58d62019-01-11 13:58:40 -0500563 // TODO: fix this check for anchor scrolling.
felkachang529bfe62018-07-04 12:51:44 +0800564 if (!mIsExpanded && isTopEntry && ambientState.getScrollY() > 0) {
565 childState.yTranslation -= ambientState.getScrollY();
566 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700567 }
Selim Cinekcafa87f2016-10-26 17:00:17 -0700568 if (row.isHeadsUpAnimatingAway()) {
569 childState.hidden = false;
570 }
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700571 }
Selim Cinek67b22602014-03-10 15:40:16 +0100572 }
573
Selim Cinek3776fe02016-02-04 13:32:43 -0800574 private void clampHunToTop(AmbientState ambientState, ExpandableNotificationRow row,
Selim Cinekbbcebde2016-11-09 18:28:20 -0800575 ExpandableViewState childState) {
Selim Cinek3776fe02016-02-04 13:32:43 -0800576 float newTranslation = Math.max(ambientState.getTopPadding()
577 + ambientState.getStackTranslation(), childState.yTranslation);
578 childState.height = (int) Math.max(childState.height - (newTranslation
Selim Cinek567e8452016-03-24 10:54:56 -0700579 - childState.yTranslation), row.getCollapsedHeight());
Selim Cinek3776fe02016-02-04 13:32:43 -0800580 childState.yTranslation = newTranslation;
581 }
582
583 private void clampHunToMaxTranslation(AmbientState ambientState, ExpandableNotificationRow row,
Selim Cinekbbcebde2016-11-09 18:28:20 -0800584 ExpandableViewState childState) {
Selim Cinek3776fe02016-02-04 13:32:43 -0800585 float newTranslation;
Selim Cinek7e0f9482017-05-22 20:00:56 -0700586 float maxHeadsUpTranslation = ambientState.getMaxHeadsUpTranslation();
587 float maxShelfPosition = ambientState.getInnerHeight() + ambientState.getTopPadding()
588 + ambientState.getStackTranslation();
589 maxHeadsUpTranslation = Math.min(maxHeadsUpTranslation, maxShelfPosition);
590 float bottomPosition = maxHeadsUpTranslation - row.getCollapsedHeight();
Selim Cinek3776fe02016-02-04 13:32:43 -0800591 newTranslation = Math.min(childState.yTranslation, bottomPosition);
Selim Cinek7e0f9482017-05-22 20:00:56 -0700592 childState.height = (int) Math.min(childState.height, maxHeadsUpTranslation
593 - newTranslation);
Selim Cinek3776fe02016-02-04 13:32:43 -0800594 childState.yTranslation = newTranslation;
Selim Cinek343e6e22014-04-11 21:23:30 +0200595 }
596
597 /**
Selim Cinek281c2022016-10-13 19:14:43 -0700598 * Clamp the height of the child down such that its end is at most on the beginning of
599 * the shelf.
600 *
Selim Cinek281c2022016-10-13 19:14:43 -0700601 * @param childViewState the view state of the child
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500602 * @param ambientState the ambient state
Selim Cinek281c2022016-10-13 19:14:43 -0700603 */
Selim Cinek2627d722018-01-19 12:16:49 -0800604 private void clampPositionToShelf(ExpandableView child,
605 ExpandableViewState childViewState,
Selim Cinek281c2022016-10-13 19:14:43 -0700606 AmbientState ambientState) {
Eliot Courtney5d3d2d02018-01-18 15:59:03 +0900607 if (ambientState.getShelf() == null) {
608 return;
609 }
610
Selim Cineka686b2c2016-10-26 13:58:27 -0700611 int shelfStart = ambientState.getInnerHeight()
612 - ambientState.getShelf().getIntrinsicHeight();
shawnlin5be1f7c2018-05-21 20:50:54 +0800613 if (ambientState.isAppearing() && !child.isAboveShelf()) {
614 // Don't show none heads-up notifications while in appearing phase.
615 childViewState.yTranslation = Math.max(childViewState.yTranslation, shelfStart);
616 }
Selim Cinek281c2022016-10-13 19:14:43 -0700617 childViewState.yTranslation = Math.min(childViewState.yTranslation, shelfStart);
Selim Cinekc383fd02016-10-21 15:31:26 -0700618 if (childViewState.yTranslation >= shelfStart) {
Selim Cinekc25989e2018-02-16 16:42:14 -0800619 childViewState.hidden = !child.isExpandAnimationRunning() && !child.hasExpandingChild();
Selim Cinekeccb5de2016-10-28 15:04:05 -0700620 childViewState.inShelf = true;
Selim Cinek9b9d6e12017-11-30 12:29:47 +0100621 childViewState.headsUpIsVisible = false;
Selim Cinekc383fd02016-10-21 15:31:26 -0700622 }
Selim Cinek281c2022016-10-13 19:14:43 -0700623 }
624
Muyuan Li87798022016-04-07 17:51:25 -0700625 protected int getMaxAllowedChildHeight(View child) {
Selim Cinek31aada42015-12-18 17:51:15 -0800626 if (child instanceof ExpandableView) {
Jorim Jaggibe565df2014-04-28 17:51:23 +0200627 ExpandableView expandableView = (ExpandableView) child;
Selim Cinek8d5727f2015-04-28 19:17:32 -0700628 return expandableView.getIntrinsicHeight();
Selim Cinek1685e632014-04-08 02:27:49 +0200629 }
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500630 return child == null ? mCollapsedSize : child.getHeight();
Selim Cinek1685e632014-04-08 02:27:49 +0200631 }
632
Selim Cinek67b22602014-03-10 15:40:16 +0100633 /**
Selim Cinek67b22602014-03-10 15:40:16 +0100634 * Calculate the Z positions for all children based on the number of items in both stacks and
635 * save it in the resultState
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500636 *
Selim Cinek67b22602014-03-10 15:40:16 +0100637 * @param algorithmState The state in which the current pass of the algorithm is currently in
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500638 * @param ambientState The ambient state of the algorithm
Selim Cinek67b22602014-03-10 15:40:16 +0100639 */
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500640 private void updateZValuesForState(StackScrollAlgorithmState algorithmState,
641 AmbientState ambientState) {
Jorim Jaggid4a57442014-04-10 02:45:55 +0200642 int childCount = algorithmState.visibleChildren.size();
Selim Cinek33223572016-02-19 19:32:22 -0800643 float childrenOnTop = 0.0f;
Selim Cinek3776fe02016-02-04 13:32:43 -0800644 for (int i = childCount - 1; i >= 0; i--) {
Selim Cinekdaab6f52017-04-06 16:46:34 -0700645 childrenOnTop = updateChildZValue(i, childrenOnTop,
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500646 algorithmState, ambientState);
Muyuan Li87798022016-04-07 17:51:25 -0700647 }
648 }
649
Selim Cinekdaab6f52017-04-06 16:46:34 -0700650 protected float updateChildZValue(int i, float childrenOnTop,
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500651 StackScrollAlgorithmState algorithmState,
Muyuan Li87798022016-04-07 17:51:25 -0700652 AmbientState ambientState) {
653 ExpandableView child = algorithmState.visibleChildren.get(i);
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500654 ExpandableViewState childViewState = child.getViewState();
Selim Cinek281c2022016-10-13 19:14:43 -0700655 int zDistanceBetweenElements = ambientState.getZDistanceBetweenElements();
656 float baseZ = ambientState.getBaseZHeight();
Selim Cinek9b9d6e12017-11-30 12:29:47 +0100657 if (child.mustStayOnScreen() && !childViewState.headsUpIsVisible
658 && !ambientState.isDozingAndNotPulsing(child)
Muyuan Li87798022016-04-07 17:51:25 -0700659 && childViewState.yTranslation < ambientState.getTopPadding()
660 + ambientState.getStackTranslation()) {
661 if (childrenOnTop != 0.0f) {
662 childrenOnTop++;
663 } else {
664 float overlap = ambientState.getTopPadding()
665 + ambientState.getStackTranslation() - childViewState.yTranslation;
666 childrenOnTop += Math.min(1.0f, overlap / childViewState.height);
667 }
Selim Cinek281c2022016-10-13 19:14:43 -0700668 childViewState.zTranslation = baseZ
669 + childrenOnTop * zDistanceBetweenElements;
Selim Cinek459aee32019-02-20 11:18:56 -0800670 } else if (i == 0 && child.isAboveShelf()) {
Selim Cinekd127d792016-11-01 19:11:41 -0700671 // In case this is a new view that has never been measured before, we don't want to
672 // elevate if we are currently expanded more then the notification
Eliot Courtney5d3d2d02018-01-18 15:59:03 +0900673 int shelfHeight = ambientState.getShelf() == null ? 0 :
674 ambientState.getShelf().getIntrinsicHeight();
Selim Cinekd127d792016-11-01 19:11:41 -0700675 float shelfStart = ambientState.getInnerHeight()
676 - shelfHeight + ambientState.getTopPadding()
677 + ambientState.getStackTranslation();
678 float notificationEnd = childViewState.yTranslation + child.getPinnedHeadsUpHeight()
679 + mPaddingBetweenElements;
680 if (shelfStart > notificationEnd) {
681 childViewState.zTranslation = baseZ;
682 } else {
683 float factor = (notificationEnd - shelfStart) / shelfHeight;
684 factor = Math.min(factor, 1.0f);
685 childViewState.zTranslation = baseZ + factor * zDistanceBetweenElements;
686 }
Muyuan Li87798022016-04-07 17:51:25 -0700687 } else {
Selim Cinek281c2022016-10-13 19:14:43 -0700688 childViewState.zTranslation = baseZ;
Selim Cinek67b22602014-03-10 15:40:16 +0100689 }
Selim Cinek99e9adf2018-03-15 09:17:47 -0700690
691 // We need to scrim the notification more from its surrounding content when we are pinned,
692 // and we therefore elevate it higher.
693 // We can use the headerVisibleAmount for this, since the value nicely goes from 0 to 1 when
694 // expanding after which we have a normal elevation again.
695 childViewState.zTranslation += (1.0f - child.getHeaderVisibleAmount())
696 * mPinnedZTranslationExtra;
Selim Cinekdaab6f52017-04-06 16:46:34 -0700697 return childrenOnTop;
Selim Cinek67b22602014-03-10 15:40:16 +0100698 }
699
Selim Cinek1685e632014-04-08 02:27:49 +0200700 public void setIsExpanded(boolean isExpanded) {
701 this.mIsExpanded = isExpanded;
702 }
703
Selim Cinek281c2022016-10-13 19:14:43 -0700704 public class StackScrollAlgorithmState {
Selim Cinek67b22602014-03-10 15:40:16 +0100705
706 /**
Gus Prevas0fa58d62019-01-11 13:58:40 -0500707 * The scroll position of the algorithm (absolute scrolling).
Selim Cinek67b22602014-03-10 15:40:16 +0100708 */
709 public int scrollY;
710
Gus Prevas0fa58d62019-01-11 13:58:40 -0500711 /** The index of the anchor view (anchor scrolling). */
712 public int anchorViewIndex;
713
714 /**
715 * The Y position, relative to the top of the screen, of the anchor view (anchor scrolling).
716 */
717 public int anchorViewY;
718
Selim Cinek67b22602014-03-10 15:40:16 +0100719 /**
Jorim Jaggid4a57442014-04-10 02:45:55 +0200720 * The children from the host view which are not gone.
721 */
Jorim Jaggibe565df2014-04-28 17:51:23 +0200722 public final ArrayList<ExpandableView> visibleChildren = new ArrayList<ExpandableView>();
Selim Cinek61633a82016-01-25 15:54:10 -0800723
724 /**
Selim Cineka7ed2c12017-01-23 20:47:24 -0800725 * The padding after each child measured in pixels.
Selim Cinek61633a82016-01-25 15:54:10 -0800726 */
Selim Cineka7ed2c12017-01-23 20:47:24 -0800727 public final HashMap<ExpandableView, Float> paddingMap = new HashMap<>();
Selim Cinek2627d722018-01-19 12:16:49 -0800728 private int indexOfExpandingNotification;
Selim Cinek281c2022016-10-13 19:14:43 -0700729
730 public int getPaddingAfterChild(ExpandableView child) {
Selim Cineka7ed2c12017-01-23 20:47:24 -0800731 Float padding = paddingMap.get(child);
732 if (padding == null) {
733 // Should only happen for the last view
734 return mPaddingBetweenElements;
735 }
736 return (int) padding.floatValue();
Selim Cinek281c2022016-10-13 19:14:43 -0700737 }
Selim Cinek2627d722018-01-19 12:16:49 -0800738
739 public int getIndexOfExpandingNotification() {
740 return indexOfExpandingNotification;
741 }
Selim Cinek67b22602014-03-10 15:40:16 +0100742 }
743
Ned Burns9eb06332019-04-23 16:02:12 -0400744 /**
745 * Interface for telling the SSA when a new notification section begins (so it can add in
746 * appropriate margins).
747 */
748 public interface SectionProvider {
749 /**
750 * True if this view starts a new "section" of notifications, such as the gentle
751 * notifications section. False if sections are not enabled.
752 */
753 boolean beginsSection(View view);
754 }
Selim Cinek67b22602014-03-10 15:40:16 +0100755}