167b2260093774f5866f781aede52830440f4ed0eSelim Cinek/* 267b2260093774f5866f781aede52830440f4ed0eSelim Cinek * Copyright (C) 2014 The Android Open Source Project 367b2260093774f5866f781aede52830440f4ed0eSelim Cinek * 467b2260093774f5866f781aede52830440f4ed0eSelim Cinek * Licensed under the Apache License, Version 2.0 (the "License"); 567b2260093774f5866f781aede52830440f4ed0eSelim Cinek * you may not use this file except in compliance with the License. 667b2260093774f5866f781aede52830440f4ed0eSelim Cinek * You may obtain a copy of the License at 767b2260093774f5866f781aede52830440f4ed0eSelim Cinek * 867b2260093774f5866f781aede52830440f4ed0eSelim Cinek * http://www.apache.org/licenses/LICENSE-2.0 967b2260093774f5866f781aede52830440f4ed0eSelim Cinek * 1067b2260093774f5866f781aede52830440f4ed0eSelim Cinek * Unless required by applicable law or agreed to in writing, software 1167b2260093774f5866f781aede52830440f4ed0eSelim Cinek * distributed under the License is distributed on an "AS IS" BASIS, 1267b2260093774f5866f781aede52830440f4ed0eSelim Cinek * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1367b2260093774f5866f781aede52830440f4ed0eSelim Cinek * See the License for the specific language governing permissions and 1467b2260093774f5866f781aede52830440f4ed0eSelim Cinek * limitations under the License 1567b2260093774f5866f781aede52830440f4ed0eSelim Cinek */ 1667b2260093774f5866f781aede52830440f4ed0eSelim Cinek 1767b2260093774f5866f781aede52830440f4ed0eSelim Cinekpackage com.android.systemui.statusbar.stack; 1867b2260093774f5866f781aede52830440f4ed0eSelim Cinek 1967b2260093774f5866f781aede52830440f4ed0eSelim Cinekimport android.content.Context; 20d7c1fae12ef0b31c225ef130e6b06445b5af53a9Jorim Jaggiimport android.util.DisplayMetrics; 216e3ecebcec1b82fd81f6d78b8deb5c4189b6026eChristoph Studerimport android.util.Log; 2267b2260093774f5866f781aede52830440f4ed0eSelim Cinekimport android.view.View; 2367b2260093774f5866f781aede52830440f4ed0eSelim Cinekimport android.view.ViewGroup; 24343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek 2567b2260093774f5866f781aede52830440f4ed0eSelim Cinekimport com.android.systemui.R; 261685e634fb0b14033bd436af8d7174436699ffecSelim Cinekimport com.android.systemui.statusbar.ExpandableNotificationRow; 27be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggiimport com.android.systemui.statusbar.ExpandableView; 2867b2260093774f5866f781aede52830440f4ed0eSelim Cinek 29d4a57440ca5fc8461959176475b0fcd8a6e05871Jorim Jaggiimport java.util.ArrayList; 30d4a57440ca5fc8461959176475b0fcd8a6e05871Jorim Jaggi 3167b2260093774f5866f781aede52830440f4ed0eSelim Cinek/** 3267b2260093774f5866f781aede52830440f4ed0eSelim Cinek * The Algorithm of the {@link com.android.systemui.statusbar.stack 3367b2260093774f5866f781aede52830440f4ed0eSelim Cinek * .NotificationStackScrollLayout} which can be queried for {@link com.android.systemui.statusbar 3467b2260093774f5866f781aede52830440f4ed0eSelim Cinek * .stack.StackScrollState} 3567b2260093774f5866f781aede52830440f4ed0eSelim Cinek */ 3667b2260093774f5866f781aede52830440f4ed0eSelim Cinekpublic class StackScrollAlgorithm { 3767b2260093774f5866f781aede52830440f4ed0eSelim Cinek 386e3ecebcec1b82fd81f6d78b8deb5c4189b6026eChristoph Studer private static final String LOG_TAG = "StackScrollAlgorithm"; 396e3ecebcec1b82fd81f6d78b8deb5c4189b6026eChristoph Studer 4067b2260093774f5866f781aede52830440f4ed0eSelim Cinek private static final int MAX_ITEMS_IN_BOTTOM_STACK = 3; 4167b2260093774f5866f781aede52830440f4ed0eSelim Cinek private static final int MAX_ITEMS_IN_TOP_STACK = 3; 4267b2260093774f5866f781aede52830440f4ed0eSelim Cinek 43362dd6d632f0eb63de4edf4a6eec281342391d9fJorim Jaggi public static final float DIMMED_SCALE = 0.95f; 44d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi 4567b2260093774f5866f781aede52830440f4ed0eSelim Cinek private int mPaddingBetweenElements; 4667b2260093774f5866f781aede52830440f4ed0eSelim Cinek private int mCollapsedSize; 4767b2260093774f5866f781aede52830440f4ed0eSelim Cinek private int mTopStackPeekSize; 4867b2260093774f5866f781aede52830440f4ed0eSelim Cinek private int mBottomStackPeekSize; 4967b2260093774f5866f781aede52830440f4ed0eSelim Cinek private int mZDistanceBetweenElements; 5067b2260093774f5866f781aede52830440f4ed0eSelim Cinek private int mZBasicHeight; 51708a6c120da6750d281195ef15a240a5627efed4Selim Cinek private int mRoundedRectCornerRadius; 5267b2260093774f5866f781aede52830440f4ed0eSelim Cinek 5367b2260093774f5866f781aede52830440f4ed0eSelim Cinek private StackIndentationFunctor mTopStackIndentationFunctor; 5467b2260093774f5866f781aede52830440f4ed0eSelim Cinek private StackIndentationFunctor mBottomStackIndentationFunctor; 5567b2260093774f5866f781aede52830440f4ed0eSelim Cinek 56343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek private int mLayoutHeight; 578c1a44b62f82c956cbe4aa0809cbdf255d0fae1fJorim Jaggi 588c1a44b62f82c956cbe4aa0809cbdf255d0fae1fJorim Jaggi /** mLayoutHeight - mTopPadding */ 598c1a44b62f82c956cbe4aa0809cbdf255d0fae1fJorim Jaggi private int mInnerHeight; 608c1a44b62f82c956cbe4aa0809cbdf255d0fae1fJorim Jaggi private int mTopPadding; 6167b2260093774f5866f781aede52830440f4ed0eSelim Cinek private StackScrollAlgorithmState mTempAlgorithmState = new StackScrollAlgorithmState(); 621685e634fb0b14033bd436af8d7174436699ffecSelim Cinek private boolean mIsExpansionChanging; 631685e634fb0b14033bd436af8d7174436699ffecSelim Cinek private int mFirstChildMaxHeight; 641685e634fb0b14033bd436af8d7174436699ffecSelim Cinek private boolean mIsExpanded; 65be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi private ExpandableView mFirstChildWhileExpanding; 661685e634fb0b14033bd436af8d7174436699ffecSelim Cinek private boolean mExpandedOnStart; 67343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek private int mTopStackTotalSize; 6834c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek private int mPaddingBetweenElementsDimmed; 6934c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek private int mPaddingBetweenElementsNormal; 7034c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek private int mBottomStackSlowDownLength; 71ad3e5afdd2dad5ae9144db1cdadd968ba6f306b1Selim Cinek private int mTopStackSlowDownLength; 72d83771ee46076d74fa7284a5a5867bc9b0ce20beSelim Cinek private int mCollapseSecondCardPadding; 733afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek private boolean mIsSmallScreen; 743afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek private int mMaxNotificationHeight; 75d7c1fae12ef0b31c225ef130e6b06445b5af53a9Jorim Jaggi private boolean mScaleDimmed; 7667b2260093774f5866f781aede52830440f4ed0eSelim Cinek 7767b2260093774f5866f781aede52830440f4ed0eSelim Cinek public StackScrollAlgorithm(Context context) { 7867b2260093774f5866f781aede52830440f4ed0eSelim Cinek initConstants(context); 7934c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek updatePadding(false); 8034c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek } 8134c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek 8234c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek private void updatePadding(boolean dimmed) { 83d7c1fae12ef0b31c225ef130e6b06445b5af53a9Jorim Jaggi mPaddingBetweenElements = dimmed && mScaleDimmed 8434c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek ? mPaddingBetweenElementsDimmed 8534c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek : mPaddingBetweenElementsNormal; 86ad3e5afdd2dad5ae9144db1cdadd968ba6f306b1Selim Cinek mTopStackTotalSize = mTopStackSlowDownLength + mPaddingBetweenElements 87ad3e5afdd2dad5ae9144db1cdadd968ba6f306b1Selim Cinek + mTopStackPeekSize; 8834c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek mTopStackIndentationFunctor = new PiecewiseLinearIndentationFunctor( 8934c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek MAX_ITEMS_IN_TOP_STACK, 9034c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek mTopStackPeekSize, 91b96924ddbf5283954f6f0f3af599844be1da1384Selim Cinek mTopStackTotalSize - mTopStackPeekSize, 9234c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek 0.5f); 9334c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek mBottomStackIndentationFunctor = new PiecewiseLinearIndentationFunctor( 9434c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek MAX_ITEMS_IN_BOTTOM_STACK, 9534c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek mBottomStackPeekSize, 9634c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek getBottomStackSlowDownLength(), 9734c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek 0.5f); 9834c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek } 9934c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek 10034c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek public int getBottomStackSlowDownLength() { 10134c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek return mBottomStackSlowDownLength + mPaddingBetweenElements; 10267b2260093774f5866f781aede52830440f4ed0eSelim Cinek } 10367b2260093774f5866f781aede52830440f4ed0eSelim Cinek 10467b2260093774f5866f781aede52830440f4ed0eSelim Cinek private void initConstants(Context context) { 10534c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek mPaddingBetweenElementsDimmed = context.getResources() 10634c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek .getDimensionPixelSize(R.dimen.notification_padding_dimmed); 10734c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek mPaddingBetweenElementsNormal = context.getResources() 108fe40f7d13bfc1faa35c9a131ce4be5104cb8f6b9Jorim Jaggi .getDimensionPixelSize(R.dimen.notification_padding); 10967b2260093774f5866f781aede52830440f4ed0eSelim Cinek mCollapsedSize = context.getResources() 110fe40f7d13bfc1faa35c9a131ce4be5104cb8f6b9Jorim Jaggi .getDimensionPixelSize(R.dimen.notification_min_height); 1113afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek mMaxNotificationHeight = context.getResources() 1123afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek .getDimensionPixelSize(R.dimen.notification_max_height); 11367b2260093774f5866f781aede52830440f4ed0eSelim Cinek mTopStackPeekSize = context.getResources() 11467b2260093774f5866f781aede52830440f4ed0eSelim Cinek .getDimensionPixelSize(R.dimen.top_stack_peek_amount); 11567b2260093774f5866f781aede52830440f4ed0eSelim Cinek mBottomStackPeekSize = context.getResources() 11667b2260093774f5866f781aede52830440f4ed0eSelim Cinek .getDimensionPixelSize(R.dimen.bottom_stack_peek_amount); 11767b2260093774f5866f781aede52830440f4ed0eSelim Cinek mZDistanceBetweenElements = context.getResources() 11867b2260093774f5866f781aede52830440f4ed0eSelim Cinek .getDimensionPixelSize(R.dimen.z_distance_between_notifications); 11967b2260093774f5866f781aede52830440f4ed0eSelim Cinek mZBasicHeight = (MAX_ITEMS_IN_BOTTOM_STACK + 1) * mZDistanceBetweenElements; 12034c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek mBottomStackSlowDownLength = context.getResources() 12134c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek .getDimensionPixelSize(R.dimen.bottom_stack_slow_down_length); 122ad3e5afdd2dad5ae9144db1cdadd968ba6f306b1Selim Cinek mTopStackSlowDownLength = context.getResources() 123ad3e5afdd2dad5ae9144db1cdadd968ba6f306b1Selim Cinek .getDimensionPixelSize(R.dimen.top_stack_slow_down_length); 124708a6c120da6750d281195ef15a240a5627efed4Selim Cinek mRoundedRectCornerRadius = context.getResources().getDimensionPixelSize( 125697178b1244533adb0ffb3325c0a27a1fde6eacaSelim Cinek R.dimen.notification_material_rounded_rect_radius); 126d83771ee46076d74fa7284a5a5867bc9b0ce20beSelim Cinek mCollapseSecondCardPadding = context.getResources().getDimensionPixelSize( 127d83771ee46076d74fa7284a5a5867bc9b0ce20beSelim Cinek R.dimen.notification_collapse_second_card_padding); 128d7c1fae12ef0b31c225ef130e6b06445b5af53a9Jorim Jaggi mScaleDimmed = context.getResources().getDisplayMetrics().densityDpi 129d7c1fae12ef0b31c225ef130e6b06445b5af53a9Jorim Jaggi >= DisplayMetrics.DENSITY_XXHIGH; 13067b2260093774f5866f781aede52830440f4ed0eSelim Cinek } 13167b2260093774f5866f781aede52830440f4ed0eSelim Cinek 132d7c1fae12ef0b31c225ef130e6b06445b5af53a9Jorim Jaggi public boolean shouldScaleDimmed() { 133d7c1fae12ef0b31c225ef130e6b06445b5af53a9Jorim Jaggi return mScaleDimmed; 134d7c1fae12ef0b31c225ef130e6b06445b5af53a9Jorim Jaggi } 13567b2260093774f5866f781aede52830440f4ed0eSelim Cinek 136d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi public void getStackScrollState(AmbientState ambientState, StackScrollState resultState) { 13767b2260093774f5866f781aede52830440f4ed0eSelim Cinek // The state of the local variables are saved in an algorithmState to easily subdivide it 13867b2260093774f5866f781aede52830440f4ed0eSelim Cinek // into multiple phases. 13967b2260093774f5866f781aede52830440f4ed0eSelim Cinek StackScrollAlgorithmState algorithmState = mTempAlgorithmState; 14067b2260093774f5866f781aede52830440f4ed0eSelim Cinek 14167b2260093774f5866f781aede52830440f4ed0eSelim Cinek // First we reset the view states to their default values. 14267b2260093774f5866f781aede52830440f4ed0eSelim Cinek resultState.resetViewStates(); 14367b2260093774f5866f781aede52830440f4ed0eSelim Cinek 144343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek algorithmState.itemsInTopStack = 0.0f; 14567b2260093774f5866f781aede52830440f4ed0eSelim Cinek algorithmState.partialInTop = 0.0f; 14667b2260093774f5866f781aede52830440f4ed0eSelim Cinek algorithmState.lastTopStackIndex = 0; 147343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek algorithmState.scrolledPixelsTop = 0; 14867b2260093774f5866f781aede52830440f4ed0eSelim Cinek algorithmState.itemsInBottomStack = 0.0f; 149343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek algorithmState.partialInBottom = 0.0f; 1508d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek float bottomOverScroll = ambientState.getOverScrollAmount(false /* onTop */); 1511408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek 1521408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek int scrollY = ambientState.getScrollY(); 1531408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek 1541408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek // Due to the overScroller, the stackscroller can have negative scroll state. This is 1551408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek // already accounted for by the top padding and doesn't need an additional adaption 1561408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek scrollY = Math.max(0, scrollY); 1571408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek algorithmState.scrollY = (int) (scrollY + mCollapsedSize + bottomOverScroll); 158343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek 159d4a57440ca5fc8461959176475b0fcd8a6e05871Jorim Jaggi updateVisibleChildren(resultState, algorithmState); 16067b2260093774f5866f781aede52830440f4ed0eSelim Cinek 16167b2260093774f5866f781aede52830440f4ed0eSelim Cinek // Phase 1: 16267b2260093774f5866f781aede52830440f4ed0eSelim Cinek findNumberOfItemsInTopStackAndUpdateState(resultState, algorithmState); 16367b2260093774f5866f781aede52830440f4ed0eSelim Cinek 16467b2260093774f5866f781aede52830440f4ed0eSelim Cinek // Phase 2: 16567b2260093774f5866f781aede52830440f4ed0eSelim Cinek updatePositionsForState(resultState, algorithmState); 16667b2260093774f5866f781aede52830440f4ed0eSelim Cinek 16767b2260093774f5866f781aede52830440f4ed0eSelim Cinek // Phase 3: 16867b2260093774f5866f781aede52830440f4ed0eSelim Cinek updateZValuesForState(resultState, algorithmState); 169eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 170d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi handleDraggedViews(ambientState, resultState, algorithmState); 171ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi updateDimmedActivatedHideSensitive(ambientState, resultState, algorithmState); 172708a6c120da6750d281195ef15a240a5627efed4Selim Cinek updateClipping(resultState, algorithmState); 1733d2b94bf8e32640e57573ebb17911b1db9440231Selim Cinek updateSpeedBumpState(resultState, algorithmState, ambientState.getSpeedBumpIndex()); 1743d2b94bf8e32640e57573ebb17911b1db9440231Selim Cinek } 1753d2b94bf8e32640e57573ebb17911b1db9440231Selim Cinek 1763d2b94bf8e32640e57573ebb17911b1db9440231Selim Cinek private void updateSpeedBumpState(StackScrollState resultState, 1773d2b94bf8e32640e57573ebb17911b1db9440231Selim Cinek StackScrollAlgorithmState algorithmState, int speedBumpIndex) { 1783d2b94bf8e32640e57573ebb17911b1db9440231Selim Cinek int childCount = algorithmState.visibleChildren.size(); 1793d2b94bf8e32640e57573ebb17911b1db9440231Selim Cinek for (int i = 0; i < childCount; i++) { 1803d2b94bf8e32640e57573ebb17911b1db9440231Selim Cinek View child = algorithmState.visibleChildren.get(i); 1813d2b94bf8e32640e57573ebb17911b1db9440231Selim Cinek StackScrollState.ViewState childViewState = resultState.getViewStateForView(child); 1823107cfacd34ded2508ab03c896e1ce894de0c795Selim Cinek 1833107cfacd34ded2508ab03c896e1ce894de0c795Selim Cinek // The speed bump can also be gone, so equality needs to be taken when comparing 1843107cfacd34ded2508ab03c896e1ce894de0c795Selim Cinek // indices. 1853107cfacd34ded2508ab03c896e1ce894de0c795Selim Cinek childViewState.belowSpeedBump = speedBumpIndex != -1 && i >= speedBumpIndex; 1863d2b94bf8e32640e57573ebb17911b1db9440231Selim Cinek } 187f54090e9bb23e9ed1b4d9e500d856f80d2fbe775Selim Cinek } 188f54090e9bb23e9ed1b4d9e500d856f80d2fbe775Selim Cinek 189708a6c120da6750d281195ef15a240a5627efed4Selim Cinek private void updateClipping(StackScrollState resultState, 190708a6c120da6750d281195ef15a240a5627efed4Selim Cinek StackScrollAlgorithmState algorithmState) { 191708a6c120da6750d281195ef15a240a5627efed4Selim Cinek float previousNotificationEnd = 0; 192708a6c120da6750d281195ef15a240a5627efed4Selim Cinek float previousNotificationStart = 0; 193708a6c120da6750d281195ef15a240a5627efed4Selim Cinek boolean previousNotificationIsSwiped = false; 194708a6c120da6750d281195ef15a240a5627efed4Selim Cinek int childCount = algorithmState.visibleChildren.size(); 195708a6c120da6750d281195ef15a240a5627efed4Selim Cinek for (int i = 0; i < childCount; i++) { 196708a6c120da6750d281195ef15a240a5627efed4Selim Cinek ExpandableView child = algorithmState.visibleChildren.get(i); 197708a6c120da6750d281195ef15a240a5627efed4Selim Cinek StackScrollState.ViewState state = resultState.getViewStateForView(child); 1982e34ec3cbbe595b646bd7f319fb369a37191847fJorim Jaggi float newYTranslation = state.yTranslation + state.height * (1f - state.scale) / 2f; 1992e34ec3cbbe595b646bd7f319fb369a37191847fJorim Jaggi float newHeight = state.height * state.scale; 200708a6c120da6750d281195ef15a240a5627efed4Selim Cinek // apply clipping and shadow 201708a6c120da6750d281195ef15a240a5627efed4Selim Cinek float newNotificationEnd = newYTranslation + newHeight; 202708a6c120da6750d281195ef15a240a5627efed4Selim Cinek 203c5baa3eb0893cb764e7810f8c68e89b04653df86Selim Cinek float clipHeight; 204c5baa3eb0893cb764e7810f8c68e89b04653df86Selim Cinek if (previousNotificationIsSwiped) { 205c5baa3eb0893cb764e7810f8c68e89b04653df86Selim Cinek // When the previous notification is swiped, we don't clip the content to the 206c5baa3eb0893cb764e7810f8c68e89b04653df86Selim Cinek // bottom of it. 207c5baa3eb0893cb764e7810f8c68e89b04653df86Selim Cinek clipHeight = newHeight; 208c5baa3eb0893cb764e7810f8c68e89b04653df86Selim Cinek } else { 209c5baa3eb0893cb764e7810f8c68e89b04653df86Selim Cinek clipHeight = newNotificationEnd - previousNotificationEnd; 210c5baa3eb0893cb764e7810f8c68e89b04653df86Selim Cinek clipHeight = Math.max(0.0f, clipHeight); 211c5baa3eb0893cb764e7810f8c68e89b04653df86Selim Cinek if (clipHeight != 0.0f) { 212c5baa3eb0893cb764e7810f8c68e89b04653df86Selim Cinek 213c5baa3eb0893cb764e7810f8c68e89b04653df86Selim Cinek // In the unlocked shade we have to clip a little bit higher because of the rounded 214c5baa3eb0893cb764e7810f8c68e89b04653df86Selim Cinek // corners of the notifications, but only if we are not fully overlapped by 215c5baa3eb0893cb764e7810f8c68e89b04653df86Selim Cinek // the top card. 216c5baa3eb0893cb764e7810f8c68e89b04653df86Selim Cinek float clippingCorrection = state.dimmed 217c5baa3eb0893cb764e7810f8c68e89b04653df86Selim Cinek ? 0 218c5baa3eb0893cb764e7810f8c68e89b04653df86Selim Cinek : mRoundedRectCornerRadius * state.scale; 219c5baa3eb0893cb764e7810f8c68e89b04653df86Selim Cinek clipHeight += clippingCorrection; 220c5baa3eb0893cb764e7810f8c68e89b04653df86Selim Cinek } 221c5baa3eb0893cb764e7810f8c68e89b04653df86Selim Cinek } 222708a6c120da6750d281195ef15a240a5627efed4Selim Cinek 223708a6c120da6750d281195ef15a240a5627efed4Selim Cinek updateChildClippingAndBackground(state, newHeight, clipHeight, 2242e34ec3cbbe595b646bd7f319fb369a37191847fJorim Jaggi newHeight - (previousNotificationStart - newYTranslation)); 225708a6c120da6750d281195ef15a240a5627efed4Selim Cinek 226708a6c120da6750d281195ef15a240a5627efed4Selim Cinek if (!child.isTransparent()) { 227708a6c120da6750d281195ef15a240a5627efed4Selim Cinek // Only update the previous values if we are not transparent, 228708a6c120da6750d281195ef15a240a5627efed4Selim Cinek // otherwise we would clip to a transparent view. 2292e34ec3cbbe595b646bd7f319fb369a37191847fJorim Jaggi previousNotificationStart = newYTranslation + state.clipTopAmount * state.scale; 230708a6c120da6750d281195ef15a240a5627efed4Selim Cinek previousNotificationEnd = newNotificationEnd; 231708a6c120da6750d281195ef15a240a5627efed4Selim Cinek previousNotificationIsSwiped = child.getTranslationX() != 0; 232708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 233708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 234708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 235708a6c120da6750d281195ef15a240a5627efed4Selim Cinek 236708a6c120da6750d281195ef15a240a5627efed4Selim Cinek /** 237708a6c120da6750d281195ef15a240a5627efed4Selim Cinek * Updates the shadow outline and the clipping for a view. 238708a6c120da6750d281195ef15a240a5627efed4Selim Cinek * 239708a6c120da6750d281195ef15a240a5627efed4Selim Cinek * @param state the viewState to update 240708a6c120da6750d281195ef15a240a5627efed4Selim Cinek * @param realHeight the currently applied height of the view 241708a6c120da6750d281195ef15a240a5627efed4Selim Cinek * @param clipHeight the desired clip height, the rest of the view will be clipped from the top 242708a6c120da6750d281195ef15a240a5627efed4Selim Cinek * @param backgroundHeight the desired background height. The shadows of the view will be 243708a6c120da6750d281195ef15a240a5627efed4Selim Cinek * based on this height and the content will be clipped from the top 244708a6c120da6750d281195ef15a240a5627efed4Selim Cinek */ 2452e34ec3cbbe595b646bd7f319fb369a37191847fJorim Jaggi private void updateChildClippingAndBackground(StackScrollState.ViewState state, 2462e34ec3cbbe595b646bd7f319fb369a37191847fJorim Jaggi float realHeight, float clipHeight, float backgroundHeight) { 247708a6c120da6750d281195ef15a240a5627efed4Selim Cinek if (realHeight > clipHeight) { 2482e34ec3cbbe595b646bd7f319fb369a37191847fJorim Jaggi // Rather overlap than create a hole. 2492e34ec3cbbe595b646bd7f319fb369a37191847fJorim Jaggi state.topOverLap = (int) Math.floor((realHeight - clipHeight) / state.scale); 250708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } else { 251708a6c120da6750d281195ef15a240a5627efed4Selim Cinek state.topOverLap = 0; 252708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 253708a6c120da6750d281195ef15a240a5627efed4Selim Cinek if (realHeight > backgroundHeight) { 2542e34ec3cbbe595b646bd7f319fb369a37191847fJorim Jaggi // Rather overlap than create a hole. 2552e34ec3cbbe595b646bd7f319fb369a37191847fJorim Jaggi state.clipTopAmount = (int) Math.floor((realHeight - backgroundHeight) / state.scale); 256708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } else { 257708a6c120da6750d281195ef15a240a5627efed4Selim Cinek state.clipTopAmount = 0; 258708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 259d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 260d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi 261d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi /** 262ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi * Updates the dimmed, activated and hiding sensitive states of the children. 263d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi */ 264ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi private void updateDimmedActivatedHideSensitive(AmbientState ambientState, 265ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi StackScrollState resultState, StackScrollAlgorithmState algorithmState) { 266d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi boolean dimmed = ambientState.isDimmed(); 267bf370992508c55d1f2493923bdc1834a0710e4baJohn Spurlock boolean dark = ambientState.isDark(); 268ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi boolean hideSensitive = ambientState.isHideSensitive(); 269d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi View activatedChild = ambientState.getActivatedChild(); 270d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi int childCount = algorithmState.visibleChildren.size(); 271d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi for (int i = 0; i < childCount; i++) { 272d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi View child = algorithmState.visibleChildren.get(i); 273d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi StackScrollState.ViewState childViewState = resultState.getViewStateForView(child); 274d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi childViewState.dimmed = dimmed; 275bf370992508c55d1f2493923bdc1834a0710e4baJohn Spurlock childViewState.dark = dark; 276ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi childViewState.hideSensitive = hideSensitive; 277b89de4ec490c2c47a7aebf2caa4c9cdd5ed1010bSelim Cinek boolean isActivatedChild = activatedChild == child; 278d7c1fae12ef0b31c225ef130e6b06445b5af53a9Jorim Jaggi childViewState.scale = !mScaleDimmed || !dimmed || isActivatedChild 279d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi ? 1.0f 280d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi : DIMMED_SCALE; 2814538cee46d3aa91c85a453049d2b2284b799c66aJorim Jaggi if (dimmed && isActivatedChild) { 2824538cee46d3aa91c85a453049d2b2284b799c66aJorim Jaggi childViewState.zTranslation += 2.0f * mZDistanceBetweenElements; 283d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 284d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 285eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 286eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 287eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek /** 288eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * Handle the special state when views are being dragged 289eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek */ 290d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi private void handleDraggedViews(AmbientState ambientState, StackScrollState resultState, 291eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek StackScrollAlgorithmState algorithmState) { 292d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi ArrayList<View> draggedViews = ambientState.getDraggedViews(); 293d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi for (View draggedView : draggedViews) { 294eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek int childIndex = algorithmState.visibleChildren.indexOf(draggedView); 295eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (childIndex >= 0 && childIndex < algorithmState.visibleChildren.size() - 1) { 296eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek View nextChild = algorithmState.visibleChildren.get(childIndex + 1); 297d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi if (!draggedViews.contains(nextChild)) { 298eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // only if the view is not dragged itself we modify its state to be fully 299eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // visible 300eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek StackScrollState.ViewState viewState = resultState.getViewStateForView( 301eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek nextChild); 302eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // The child below the dragged one must be fully visible 303eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek viewState.alpha = 1; 304eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 305eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 306eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // Lets set the alpha to the one it currently has, as its currently being dragged 307eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek StackScrollState.ViewState viewState = resultState.getViewStateForView(draggedView); 308eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // The dragged child should keep the set alpha 309eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek viewState.alpha = draggedView.getAlpha(); 310eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 311eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 312343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek } 313343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek 314343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek /** 315d4a57440ca5fc8461959176475b0fcd8a6e05871Jorim Jaggi * Update the visible children on the state. 316d4a57440ca5fc8461959176475b0fcd8a6e05871Jorim Jaggi */ 317d4a57440ca5fc8461959176475b0fcd8a6e05871Jorim Jaggi private void updateVisibleChildren(StackScrollState resultState, 318d4a57440ca5fc8461959176475b0fcd8a6e05871Jorim Jaggi StackScrollAlgorithmState state) { 319d4a57440ca5fc8461959176475b0fcd8a6e05871Jorim Jaggi ViewGroup hostView = resultState.getHostView(); 320d4a57440ca5fc8461959176475b0fcd8a6e05871Jorim Jaggi int childCount = hostView.getChildCount(); 321d4a57440ca5fc8461959176475b0fcd8a6e05871Jorim Jaggi state.visibleChildren.clear(); 322d4a57440ca5fc8461959176475b0fcd8a6e05871Jorim Jaggi state.visibleChildren.ensureCapacity(childCount); 323d4a57440ca5fc8461959176475b0fcd8a6e05871Jorim Jaggi for (int i = 0; i < childCount; i++) { 324be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi ExpandableView v = (ExpandableView) hostView.getChildAt(i); 325d4a57440ca5fc8461959176475b0fcd8a6e05871Jorim Jaggi if (v.getVisibility() != View.GONE) { 3268efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek StackScrollState.ViewState viewState = resultState.getViewStateForView(v); 3278efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek viewState.notGoneIndex = state.visibleChildren.size(); 328d4a57440ca5fc8461959176475b0fcd8a6e05871Jorim Jaggi state.visibleChildren.add(v); 329d4a57440ca5fc8461959176475b0fcd8a6e05871Jorim Jaggi } 330d4a57440ca5fc8461959176475b0fcd8a6e05871Jorim Jaggi } 331d4a57440ca5fc8461959176475b0fcd8a6e05871Jorim Jaggi } 332d4a57440ca5fc8461959176475b0fcd8a6e05871Jorim Jaggi 333d4a57440ca5fc8461959176475b0fcd8a6e05871Jorim Jaggi /** 33467b2260093774f5866f781aede52830440f4ed0eSelim Cinek * Determine the positions for the views. This is the main part of the algorithm. 33567b2260093774f5866f781aede52830440f4ed0eSelim Cinek * 33667b2260093774f5866f781aede52830440f4ed0eSelim Cinek * @param resultState The result state to update if a change to the properties of a child occurs 33767b2260093774f5866f781aede52830440f4ed0eSelim Cinek * @param algorithmState The state in which the current pass of the algorithm is currently in 33867b2260093774f5866f781aede52830440f4ed0eSelim Cinek */ 33967b2260093774f5866f781aede52830440f4ed0eSelim Cinek private void updatePositionsForState(StackScrollState resultState, 34067b2260093774f5866f781aede52830440f4ed0eSelim Cinek StackScrollAlgorithmState algorithmState) { 34167b2260093774f5866f781aede52830440f4ed0eSelim Cinek 3421685e634fb0b14033bd436af8d7174436699ffecSelim Cinek // The starting position of the bottom stack peek 3438c1a44b62f82c956cbe4aa0809cbdf255d0fae1fJorim Jaggi float bottomPeekStart = mInnerHeight - mBottomStackPeekSize; 3441685e634fb0b14033bd436af8d7174436699ffecSelim Cinek 34567b2260093774f5866f781aede52830440f4ed0eSelim Cinek // The position where the bottom stack starts. 34634c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek float bottomStackStart = bottomPeekStart - mBottomStackSlowDownLength; 34767b2260093774f5866f781aede52830440f4ed0eSelim Cinek 34867b2260093774f5866f781aede52830440f4ed0eSelim Cinek // The y coordinate of the current child. 34967b2260093774f5866f781aede52830440f4ed0eSelim Cinek float currentYPosition = 0.0f; 35067b2260093774f5866f781aede52830440f4ed0eSelim Cinek 35167b2260093774f5866f781aede52830440f4ed0eSelim Cinek // How far in is the element currently transitioning into the bottom stack. 35267b2260093774f5866f781aede52830440f4ed0eSelim Cinek float yPositionInScrollView = 0.0f; 35367b2260093774f5866f781aede52830440f4ed0eSelim Cinek 354d4a57440ca5fc8461959176475b0fcd8a6e05871Jorim Jaggi int childCount = algorithmState.visibleChildren.size(); 35567b2260093774f5866f781aede52830440f4ed0eSelim Cinek int numberOfElementsCompletelyIn = (int) algorithmState.itemsInTopStack; 35667b2260093774f5866f781aede52830440f4ed0eSelim Cinek for (int i = 0; i < childCount; i++) { 357be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi ExpandableView child = algorithmState.visibleChildren.get(i); 35867b2260093774f5866f781aede52830440f4ed0eSelim Cinek StackScrollState.ViewState childViewState = resultState.getViewStateForView(child); 3596e3ecebcec1b82fd81f6d78b8deb5c4189b6026eChristoph Studer childViewState.location = StackScrollState.ViewState.LOCATION_UNKNOWN; 360be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi int childHeight = getMaxAllowedChildHeight(child); 36167b2260093774f5866f781aede52830440f4ed0eSelim Cinek float yPositionInScrollViewAfterElement = yPositionInScrollView 36267b2260093774f5866f781aede52830440f4ed0eSelim Cinek + childHeight 36367b2260093774f5866f781aede52830440f4ed0eSelim Cinek + mPaddingBetweenElements; 364343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek float scrollOffset = yPositionInScrollView - algorithmState.scrollY + mCollapsedSize; 365343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek 366343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek if (i == algorithmState.lastTopStackIndex + 1) { 367343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek // Normally the position of this child is the position in the regular scrollview, 368343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek // but if the two stacks are very close to each other, 369343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek // then have have to push it even more upwards to the position of the bottom 370343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek // stack start. 371343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek currentYPosition = Math.min(scrollOffset, bottomStackStart); 372343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek } 373343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek childViewState.yTranslation = currentYPosition; 374343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek 375343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek // The y position after this element 376343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek float nextYPosition = currentYPosition + childHeight + 377343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek mPaddingBetweenElements; 378343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek 379343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek if (i <= algorithmState.lastTopStackIndex) { 38067b2260093774f5866f781aede52830440f4ed0eSelim Cinek // Case 1: 38167b2260093774f5866f781aede52830440f4ed0eSelim Cinek // We are in the top Stack 382343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek updateStateForTopStackChild(algorithmState, 383343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek numberOfElementsCompletelyIn, i, childHeight, childViewState, scrollOffset); 3843afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek clampPositionToTopStackEnd(childViewState, childHeight); 3853afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek 386343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek // check if we are overlapping with the bottom stack 387343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek if (childViewState.yTranslation + childHeight + mPaddingBetweenElements 3884581cf820637301dffa7dab0d59d92929b19e9c1Selim Cinek >= bottomStackStart && !mIsExpansionChanging && i != 0 && mIsSmallScreen) { 3893afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek // we just collapse this element slightly 3903afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek int newSize = (int) Math.max(bottomStackStart - mPaddingBetweenElements - 3913afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek childViewState.yTranslation, mCollapsedSize); 3923afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek childViewState.height = newSize; 3934581cf820637301dffa7dab0d59d92929b19e9c1Selim Cinek updateStateForChildTransitioningInBottom(algorithmState, bottomStackStart, 3944581cf820637301dffa7dab0d59d92929b19e9c1Selim Cinek bottomPeekStart, childViewState.yTranslation, childViewState, 3954581cf820637301dffa7dab0d59d92929b19e9c1Selim Cinek childHeight); 396343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek } 3973afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek clampPositionToBottomStackStart(childViewState, childViewState.height); 398343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek } else if (nextYPosition >= bottomStackStart) { 39967b2260093774f5866f781aede52830440f4ed0eSelim Cinek // Case 2: 400343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek // We are in the bottom stack. 401343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek if (currentYPosition >= bottomStackStart) { 40267b2260093774f5866f781aede52830440f4ed0eSelim Cinek // According to the regular scroll view we are fully translated out of the 40367b2260093774f5866f781aede52830440f4ed0eSelim Cinek // bottom of the screen so we are fully in the bottom stack 404343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek updateStateForChildFullyInBottomStack(algorithmState, 405343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek bottomStackStart, childViewState, childHeight); 40667b2260093774f5866f781aede52830440f4ed0eSelim Cinek } else { 40767b2260093774f5866f781aede52830440f4ed0eSelim Cinek // According to the regular scroll view we are currently translating out of / 40867b2260093774f5866f781aede52830440f4ed0eSelim Cinek // into the bottom of the screen 409343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek updateStateForChildTransitioningInBottom(algorithmState, 410343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek bottomStackStart, bottomPeekStart, currentYPosition, 411343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek childViewState, childHeight); 41267b2260093774f5866f781aede52830440f4ed0eSelim Cinek } 4136e3ecebcec1b82fd81f6d78b8deb5c4189b6026eChristoph Studer } else { 414343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek // Case 3: 415343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek // We are in the regular scroll area. 4166e3ecebcec1b82fd81f6d78b8deb5c4189b6026eChristoph Studer childViewState.location = StackScrollState.ViewState.LOCATION_MAIN_AREA; 417343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek clampYTranslation(childViewState, childHeight); 4186e3ecebcec1b82fd81f6d78b8deb5c4189b6026eChristoph Studer } 419343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek 4206e3ecebcec1b82fd81f6d78b8deb5c4189b6026eChristoph Studer // The first card is always rendered. 4216e3ecebcec1b82fd81f6d78b8deb5c4189b6026eChristoph Studer if (i == 0) { 4226e3ecebcec1b82fd81f6d78b8deb5c4189b6026eChristoph Studer childViewState.alpha = 1.0f; 4238d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek childViewState.yTranslation = Math.max(mCollapsedSize - algorithmState.scrollY, 0); 424d83771ee46076d74fa7284a5a5867bc9b0ce20beSelim Cinek if (childViewState.yTranslation + childViewState.height 425d83771ee46076d74fa7284a5a5867bc9b0ce20beSelim Cinek > bottomPeekStart - mCollapseSecondCardPadding) { 4264fe3e475eaeb88ec8f0bb580ee765ceefe13bcbcSelim Cinek childViewState.height = (int) Math.max( 427d83771ee46076d74fa7284a5a5867bc9b0ce20beSelim Cinek bottomPeekStart - mCollapseSecondCardPadding 428d83771ee46076d74fa7284a5a5867bc9b0ce20beSelim Cinek - childViewState.yTranslation, mCollapsedSize); 4294fe3e475eaeb88ec8f0bb580ee765ceefe13bcbcSelim Cinek } 4306e3ecebcec1b82fd81f6d78b8deb5c4189b6026eChristoph Studer childViewState.location = StackScrollState.ViewState.LOCATION_FIRST_CARD; 4316e3ecebcec1b82fd81f6d78b8deb5c4189b6026eChristoph Studer } 4326e3ecebcec1b82fd81f6d78b8deb5c4189b6026eChristoph Studer if (childViewState.location == StackScrollState.ViewState.LOCATION_UNKNOWN) { 4336e3ecebcec1b82fd81f6d78b8deb5c4189b6026eChristoph Studer Log.wtf(LOG_TAG, "Failed to assign location for child " + i); 43467b2260093774f5866f781aede52830440f4ed0eSelim Cinek } 435343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek currentYPosition = childViewState.yTranslation + childHeight + mPaddingBetweenElements; 43667b2260093774f5866f781aede52830440f4ed0eSelim Cinek yPositionInScrollView = yPositionInScrollViewAfterElement; 4378c1a44b62f82c956cbe4aa0809cbdf255d0fae1fJorim Jaggi 4388c1a44b62f82c956cbe4aa0809cbdf255d0fae1fJorim Jaggi childViewState.yTranslation += mTopPadding; 43967b2260093774f5866f781aede52830440f4ed0eSelim Cinek } 44067b2260093774f5866f781aede52830440f4ed0eSelim Cinek } 44167b2260093774f5866f781aede52830440f4ed0eSelim Cinek 4421685e634fb0b14033bd436af8d7174436699ffecSelim Cinek /** 443343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek * Clamp the yTranslation both up and down to valid positions. 4441685e634fb0b14033bd436af8d7174436699ffecSelim Cinek * 4451685e634fb0b14033bd436af8d7174436699ffecSelim Cinek * @param childViewState the view state of the child 446343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek * @param childHeight the height of this child 4471685e634fb0b14033bd436af8d7174436699ffecSelim Cinek */ 448343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek private void clampYTranslation(StackScrollState.ViewState childViewState, int childHeight) { 449343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek clampPositionToBottomStackStart(childViewState, childHeight); 450343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek clampPositionToTopStackEnd(childViewState, childHeight); 451343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek } 452343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek 453343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek /** 454343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek * Clamp the yTranslation of the child down such that its end is at most on the beginning of 455343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek * the bottom stack. 456343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek * 457343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek * @param childViewState the view state of the child 458343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek * @param childHeight the height of this child 459343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek */ 460343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek private void clampPositionToBottomStackStart(StackScrollState.ViewState childViewState, 461343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek int childHeight) { 462343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek childViewState.yTranslation = Math.min(childViewState.yTranslation, 463d83771ee46076d74fa7284a5a5867bc9b0ce20beSelim Cinek mInnerHeight - mBottomStackPeekSize - mCollapseSecondCardPadding - childHeight); 464343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek } 465343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek 466343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek /** 467343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek * Clamp the yTranslation of the child up such that its end is at lest on the end of the top 468be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * stack.get 469343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek * 470343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek * @param childViewState the view state of the child 471343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek * @param childHeight the height of this child 472343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek */ 473343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek private void clampPositionToTopStackEnd(StackScrollState.ViewState childViewState, 474343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek int childHeight) { 475343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek childViewState.yTranslation = Math.max(childViewState.yTranslation, 476343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek mCollapsedSize - childHeight); 4771685e634fb0b14033bd436af8d7174436699ffecSelim Cinek } 4781685e634fb0b14033bd436af8d7174436699ffecSelim Cinek 4791685e634fb0b14033bd436af8d7174436699ffecSelim Cinek private int getMaxAllowedChildHeight(View child) { 4801685e634fb0b14033bd436af8d7174436699ffecSelim Cinek if (child instanceof ExpandableNotificationRow) { 4811685e634fb0b14033bd436af8d7174436699ffecSelim Cinek ExpandableNotificationRow row = (ExpandableNotificationRow) child; 4829cbadd3c08a7d7dd3412743dd04aecb16c5a1595Jorim Jaggi return row.getIntrinsicHeight(); 483be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi } else if (child instanceof ExpandableView) { 484be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi ExpandableView expandableView = (ExpandableView) child; 485be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi return expandableView.getActualHeight(); 4861685e634fb0b14033bd436af8d7174436699ffecSelim Cinek } 487be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi return child == null? mCollapsedSize : child.getHeight(); 4881685e634fb0b14033bd436af8d7174436699ffecSelim Cinek } 4891685e634fb0b14033bd436af8d7174436699ffecSelim Cinek 490343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek private void updateStateForChildTransitioningInBottom(StackScrollAlgorithmState algorithmState, 491343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek float transitioningPositionStart, float bottomPeakStart, float currentYPosition, 492343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek StackScrollState.ViewState childViewState, int childHeight) { 493343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek 494343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek // This is the transitioning element on top of bottom stack, calculate how far we are in. 49567b2260093774f5866f781aede52830440f4ed0eSelim Cinek algorithmState.partialInBottom = 1.0f - ( 496343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek (transitioningPositionStart - currentYPosition) / (childHeight + 497343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek mPaddingBetweenElements)); 498343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek 499343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek // the offset starting at the transitionPosition of the bottom stack 500343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek float offset = mBottomStackIndentationFunctor.getValue(algorithmState.partialInBottom); 50167b2260093774f5866f781aede52830440f4ed0eSelim Cinek algorithmState.itemsInBottomStack += algorithmState.partialInBottom; 5023afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek int newHeight = childHeight; 5033afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek if (childHeight > mCollapsedSize && mIsSmallScreen) { 5043afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek newHeight = (int) Math.max(Math.min(transitioningPositionStart + offset - 5053afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek mPaddingBetweenElements - currentYPosition, childHeight), mCollapsedSize); 5063afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek childViewState.height = newHeight; 5073afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek } 5083afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek childViewState.yTranslation = transitioningPositionStart + offset - newHeight 509fe40f7d13bfc1faa35c9a131ce4be5104cb8f6b9Jorim Jaggi - mPaddingBetweenElements; 5103afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek 511343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek // We want at least to be at the end of the top stack when collapsing 5123afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek clampPositionToTopStackEnd(childViewState, newHeight); 513343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek childViewState.location = StackScrollState.ViewState.LOCATION_MAIN_AREA; 51467b2260093774f5866f781aede52830440f4ed0eSelim Cinek } 51567b2260093774f5866f781aede52830440f4ed0eSelim Cinek 516343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek private void updateStateForChildFullyInBottomStack(StackScrollAlgorithmState algorithmState, 51767b2260093774f5866f781aede52830440f4ed0eSelim Cinek float transitioningPositionStart, StackScrollState.ViewState childViewState, 51867b2260093774f5866f781aede52830440f4ed0eSelim Cinek int childHeight) { 51967b2260093774f5866f781aede52830440f4ed0eSelim Cinek 520343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek float currentYPosition; 52167b2260093774f5866f781aede52830440f4ed0eSelim Cinek algorithmState.itemsInBottomStack += 1.0f; 52267b2260093774f5866f781aede52830440f4ed0eSelim Cinek if (algorithmState.itemsInBottomStack < MAX_ITEMS_IN_BOTTOM_STACK) { 52367b2260093774f5866f781aede52830440f4ed0eSelim Cinek // We are visually entering the bottom stack 524343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek currentYPosition = transitioningPositionStart 525fe40f7d13bfc1faa35c9a131ce4be5104cb8f6b9Jorim Jaggi + mBottomStackIndentationFunctor.getValue(algorithmState.itemsInBottomStack) 526fe40f7d13bfc1faa35c9a131ce4be5104cb8f6b9Jorim Jaggi - mPaddingBetweenElements; 5276e3ecebcec1b82fd81f6d78b8deb5c4189b6026eChristoph Studer childViewState.location = StackScrollState.ViewState.LOCATION_BOTTOM_STACK_PEEKING; 52867b2260093774f5866f781aede52830440f4ed0eSelim Cinek } else { 52967b2260093774f5866f781aede52830440f4ed0eSelim Cinek // we are fully inside the stack 53067b2260093774f5866f781aede52830440f4ed0eSelim Cinek if (algorithmState.itemsInBottomStack > MAX_ITEMS_IN_BOTTOM_STACK + 2) { 53167b2260093774f5866f781aede52830440f4ed0eSelim Cinek childViewState.alpha = 0.0f; 53267b2260093774f5866f781aede52830440f4ed0eSelim Cinek } else if (algorithmState.itemsInBottomStack 53367b2260093774f5866f781aede52830440f4ed0eSelim Cinek > MAX_ITEMS_IN_BOTTOM_STACK + 1) { 53467b2260093774f5866f781aede52830440f4ed0eSelim Cinek childViewState.alpha = 1.0f - algorithmState.partialInBottom; 53567b2260093774f5866f781aede52830440f4ed0eSelim Cinek } 5366e3ecebcec1b82fd81f6d78b8deb5c4189b6026eChristoph Studer childViewState.location = StackScrollState.ViewState.LOCATION_BOTTOM_STACK_HIDDEN; 5378c1a44b62f82c956cbe4aa0809cbdf255d0fae1fJorim Jaggi currentYPosition = mInnerHeight; 53867b2260093774f5866f781aede52830440f4ed0eSelim Cinek } 539343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek childViewState.yTranslation = currentYPosition - childHeight; 540343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek clampPositionToTopStackEnd(childViewState, childHeight); 54167b2260093774f5866f781aede52830440f4ed0eSelim Cinek } 54267b2260093774f5866f781aede52830440f4ed0eSelim Cinek 543343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek private void updateStateForTopStackChild(StackScrollAlgorithmState algorithmState, 544343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek int numberOfElementsCompletelyIn, int i, int childHeight, 545343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek StackScrollState.ViewState childViewState, float scrollOffset) { 54667b2260093774f5866f781aede52830440f4ed0eSelim Cinek 54767b2260093774f5866f781aede52830440f4ed0eSelim Cinek 54867b2260093774f5866f781aede52830440f4ed0eSelim Cinek // First we calculate the index relative to the current stack window of size at most 54967b2260093774f5866f781aede52830440f4ed0eSelim Cinek // {@link #MAX_ITEMS_IN_TOP_STACK} 550343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek int paddedIndex = i - 1 55167b2260093774f5866f781aede52830440f4ed0eSelim Cinek - Math.max(numberOfElementsCompletelyIn - MAX_ITEMS_IN_TOP_STACK, 0); 55267b2260093774f5866f781aede52830440f4ed0eSelim Cinek if (paddedIndex >= 0) { 553343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek 55467b2260093774f5866f781aede52830440f4ed0eSelim Cinek // We are currently visually entering the top stack 555ad3e5afdd2dad5ae9144db1cdadd968ba6f306b1Selim Cinek float distanceToStack = (childHeight + mPaddingBetweenElements) 556ad3e5afdd2dad5ae9144db1cdadd968ba6f306b1Selim Cinek - algorithmState.scrolledPixelsTop; 557ad3e5afdd2dad5ae9144db1cdadd968ba6f306b1Selim Cinek if (i == algorithmState.lastTopStackIndex 558ad3e5afdd2dad5ae9144db1cdadd968ba6f306b1Selim Cinek && distanceToStack > (mTopStackTotalSize + mPaddingBetweenElements)) { 559343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek 560343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek // Child is currently translating into stack but not yet inside slow down zone. 561343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek // Handle it like the regular scrollview. 562343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek childViewState.yTranslation = scrollOffset; 5636e3ecebcec1b82fd81f6d78b8deb5c4189b6026eChristoph Studer } else { 564343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek // Apply stacking logic. 565343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek float numItemsBefore; 566343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek if (i == algorithmState.lastTopStackIndex) { 567ad3e5afdd2dad5ae9144db1cdadd968ba6f306b1Selim Cinek numItemsBefore = 1.0f 568ad3e5afdd2dad5ae9144db1cdadd968ba6f306b1Selim Cinek - (distanceToStack / (mTopStackTotalSize + mPaddingBetweenElements)); 569343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek } else { 570343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek numItemsBefore = algorithmState.itemsInTopStack - i; 571343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek } 572343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek // The end position of the current child 573ad3e5afdd2dad5ae9144db1cdadd968ba6f306b1Selim Cinek float currentChildEndY = mCollapsedSize + mTopStackTotalSize 574ad3e5afdd2dad5ae9144db1cdadd968ba6f306b1Selim Cinek - mTopStackIndentationFunctor.getValue(numItemsBefore); 575343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek childViewState.yTranslation = currentChildEndY - childHeight; 57667b2260093774f5866f781aede52830440f4ed0eSelim Cinek } 577343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek childViewState.location = StackScrollState.ViewState.LOCATION_TOP_STACK_PEEKING; 57867b2260093774f5866f781aede52830440f4ed0eSelim Cinek } else { 579343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek if (paddedIndex == -1) { 580343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek childViewState.alpha = 1.0f - algorithmState.partialInTop; 581343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek } else { 582343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek // We are hidden behind the top card and faded out, so we can hide ourselves. 583343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek childViewState.alpha = 0.0f; 584343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek } 585343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek childViewState.yTranslation = mCollapsedSize - childHeight; 5866e3ecebcec1b82fd81f6d78b8deb5c4189b6026eChristoph Studer childViewState.location = StackScrollState.ViewState.LOCATION_TOP_STACK_HIDDEN; 58767b2260093774f5866f781aede52830440f4ed0eSelim Cinek } 588343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek 589343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek 59067b2260093774f5866f781aede52830440f4ed0eSelim Cinek } 59167b2260093774f5866f781aede52830440f4ed0eSelim Cinek 59267b2260093774f5866f781aede52830440f4ed0eSelim Cinek /** 59367b2260093774f5866f781aede52830440f4ed0eSelim Cinek * Find the number of items in the top stack and update the result state if needed. 59467b2260093774f5866f781aede52830440f4ed0eSelim Cinek * 59567b2260093774f5866f781aede52830440f4ed0eSelim Cinek * @param resultState The result state to update if a height change of an child occurs 59667b2260093774f5866f781aede52830440f4ed0eSelim Cinek * @param algorithmState The state in which the current pass of the algorithm is currently in 59767b2260093774f5866f781aede52830440f4ed0eSelim Cinek */ 59867b2260093774f5866f781aede52830440f4ed0eSelim Cinek private void findNumberOfItemsInTopStackAndUpdateState(StackScrollState resultState, 59967b2260093774f5866f781aede52830440f4ed0eSelim Cinek StackScrollAlgorithmState algorithmState) { 60067b2260093774f5866f781aede52830440f4ed0eSelim Cinek 60167b2260093774f5866f781aede52830440f4ed0eSelim Cinek // The y Position if the element would be in a regular scrollView 60267b2260093774f5866f781aede52830440f4ed0eSelim Cinek float yPositionInScrollView = 0.0f; 603d4a57440ca5fc8461959176475b0fcd8a6e05871Jorim Jaggi int childCount = algorithmState.visibleChildren.size(); 60467b2260093774f5866f781aede52830440f4ed0eSelim Cinek 60567b2260093774f5866f781aede52830440f4ed0eSelim Cinek // find the number of elements in the top stack. 60667b2260093774f5866f781aede52830440f4ed0eSelim Cinek for (int i = 0; i < childCount; i++) { 607be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi ExpandableView child = algorithmState.visibleChildren.get(i); 60867b2260093774f5866f781aede52830440f4ed0eSelim Cinek StackScrollState.ViewState childViewState = resultState.getViewStateForView(child); 609be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi int childHeight = getMaxAllowedChildHeight(child); 61067b2260093774f5866f781aede52830440f4ed0eSelim Cinek float yPositionInScrollViewAfterElement = yPositionInScrollView 61167b2260093774f5866f781aede52830440f4ed0eSelim Cinek + childHeight 61267b2260093774f5866f781aede52830440f4ed0eSelim Cinek + mPaddingBetweenElements; 61367b2260093774f5866f781aede52830440f4ed0eSelim Cinek if (yPositionInScrollView < algorithmState.scrollY) { 6148d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek if (i == 0 && algorithmState.scrollY <= mCollapsedSize) { 615343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek 616343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek // The starting position of the bottom stack peek 617d83771ee46076d74fa7284a5a5867bc9b0ce20beSelim Cinek int bottomPeekStart = mInnerHeight - mBottomStackPeekSize - 618d83771ee46076d74fa7284a5a5867bc9b0ce20beSelim Cinek mCollapseSecondCardPadding; 619343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek // Collapse and expand the first child while the shade is being expanded 620343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek float maxHeight = mIsExpansionChanging && child == mFirstChildWhileExpanding 621343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek ? mFirstChildMaxHeight 622343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek : childHeight; 623343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek childViewState.height = (int) Math.max(Math.min(bottomPeekStart, maxHeight), 624343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek mCollapsedSize); 625343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek algorithmState.itemsInTopStack = 1.0f; 626343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek 627343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek } else if (yPositionInScrollViewAfterElement < algorithmState.scrollY) { 62867b2260093774f5866f781aede52830440f4ed0eSelim Cinek // According to the regular scroll view we are fully off screen 62967b2260093774f5866f781aede52830440f4ed0eSelim Cinek algorithmState.itemsInTopStack += 1.0f; 630343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek if (i == 0) { 63167b2260093774f5866f781aede52830440f4ed0eSelim Cinek childViewState.height = mCollapsedSize; 63267b2260093774f5866f781aede52830440f4ed0eSelim Cinek } 63367b2260093774f5866f781aede52830440f4ed0eSelim Cinek } else { 63467b2260093774f5866f781aede52830440f4ed0eSelim Cinek // According to the regular scroll view we are partially off screen 635343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek 63667b2260093774f5866f781aede52830440f4ed0eSelim Cinek // How much did we scroll into this child 637ad3e5afdd2dad5ae9144db1cdadd968ba6f306b1Selim Cinek algorithmState.scrolledPixelsTop = algorithmState.scrollY 638ad3e5afdd2dad5ae9144db1cdadd968ba6f306b1Selim Cinek - yPositionInScrollView; 639343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek algorithmState.partialInTop = (algorithmState.scrolledPixelsTop) / (childHeight 64067b2260093774f5866f781aede52830440f4ed0eSelim Cinek + mPaddingBetweenElements); 64167b2260093774f5866f781aede52830440f4ed0eSelim Cinek 64267b2260093774f5866f781aede52830440f4ed0eSelim Cinek // Our element can be expanded, so this can get negative 64367b2260093774f5866f781aede52830440f4ed0eSelim Cinek algorithmState.partialInTop = Math.max(0.0f, algorithmState.partialInTop); 64467b2260093774f5866f781aede52830440f4ed0eSelim Cinek algorithmState.itemsInTopStack += algorithmState.partialInTop; 645ad3e5afdd2dad5ae9144db1cdadd968ba6f306b1Selim Cinek 646343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek if (i == 0) { 647ad3e5afdd2dad5ae9144db1cdadd968ba6f306b1Selim Cinek // If it is expanded we have to collapse it to a new size 648ad3e5afdd2dad5ae9144db1cdadd968ba6f306b1Selim Cinek float newSize = yPositionInScrollViewAfterElement 649ad3e5afdd2dad5ae9144db1cdadd968ba6f306b1Selim Cinek - mPaddingBetweenElements 650ad3e5afdd2dad5ae9144db1cdadd968ba6f306b1Selim Cinek - algorithmState.scrollY + mCollapsedSize; 651ad3e5afdd2dad5ae9144db1cdadd968ba6f306b1Selim Cinek newSize = Math.max(mCollapsedSize, newSize); 6524e456bee15e56e7cc09b62591613f0a794f077e3Selim Cinek algorithmState.itemsInTopStack = 1.0f; 65367b2260093774f5866f781aede52830440f4ed0eSelim Cinek childViewState.height = (int) newSize; 65467b2260093774f5866f781aede52830440f4ed0eSelim Cinek } 655343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek algorithmState.lastTopStackIndex = i; 656343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek break; 65767b2260093774f5866f781aede52830440f4ed0eSelim Cinek } 65867b2260093774f5866f781aede52830440f4ed0eSelim Cinek } else { 659343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek algorithmState.lastTopStackIndex = i - 1; 66067b2260093774f5866f781aede52830440f4ed0eSelim Cinek // We are already past the stack so we can end the loop 66167b2260093774f5866f781aede52830440f4ed0eSelim Cinek break; 66267b2260093774f5866f781aede52830440f4ed0eSelim Cinek } 66367b2260093774f5866f781aede52830440f4ed0eSelim Cinek yPositionInScrollView = yPositionInScrollViewAfterElement; 66467b2260093774f5866f781aede52830440f4ed0eSelim Cinek } 66567b2260093774f5866f781aede52830440f4ed0eSelim Cinek } 66667b2260093774f5866f781aede52830440f4ed0eSelim Cinek 66767b2260093774f5866f781aede52830440f4ed0eSelim Cinek /** 66867b2260093774f5866f781aede52830440f4ed0eSelim Cinek * Calculate the Z positions for all children based on the number of items in both stacks and 66967b2260093774f5866f781aede52830440f4ed0eSelim Cinek * save it in the resultState 67067b2260093774f5866f781aede52830440f4ed0eSelim Cinek * 67167b2260093774f5866f781aede52830440f4ed0eSelim Cinek * @param resultState The result state to update the zTranslation values 67267b2260093774f5866f781aede52830440f4ed0eSelim Cinek * @param algorithmState The state in which the current pass of the algorithm is currently in 67367b2260093774f5866f781aede52830440f4ed0eSelim Cinek */ 67467b2260093774f5866f781aede52830440f4ed0eSelim Cinek private void updateZValuesForState(StackScrollState resultState, 67567b2260093774f5866f781aede52830440f4ed0eSelim Cinek StackScrollAlgorithmState algorithmState) { 676d4a57440ca5fc8461959176475b0fcd8a6e05871Jorim Jaggi int childCount = algorithmState.visibleChildren.size(); 67767b2260093774f5866f781aede52830440f4ed0eSelim Cinek for (int i = 0; i < childCount; i++) { 678d4a57440ca5fc8461959176475b0fcd8a6e05871Jorim Jaggi View child = algorithmState.visibleChildren.get(i); 67967b2260093774f5866f781aede52830440f4ed0eSelim Cinek StackScrollState.ViewState childViewState = resultState.getViewStateForView(child); 68067b2260093774f5866f781aede52830440f4ed0eSelim Cinek if (i < algorithmState.itemsInTopStack) { 68167b2260093774f5866f781aede52830440f4ed0eSelim Cinek float stackIndex = algorithmState.itemsInTopStack - i; 682c5baa3eb0893cb764e7810f8c68e89b04653df86Selim Cinek 683c5baa3eb0893cb764e7810f8c68e89b04653df86Selim Cinek // Ensure that the topmost item is a little bit higher than the rest when fully 684c5baa3eb0893cb764e7810f8c68e89b04653df86Selim Cinek // scrolled, to avoid drawing errors when swiping it out 685c5baa3eb0893cb764e7810f8c68e89b04653df86Selim Cinek float max = MAX_ITEMS_IN_TOP_STACK + (i == 0 ? 2.5f : 2); 686c5baa3eb0893cb764e7810f8c68e89b04653df86Selim Cinek stackIndex = Math.min(stackIndex, max); 6874e456bee15e56e7cc09b62591613f0a794f077e3Selim Cinek if (i == 0 && algorithmState.itemsInTopStack < 2.0f) { 6884e456bee15e56e7cc09b62591613f0a794f077e3Selim Cinek 6894e456bee15e56e7cc09b62591613f0a794f077e3Selim Cinek // We only have the top item and an additional item in the top stack, 6904e456bee15e56e7cc09b62591613f0a794f077e3Selim Cinek // Interpolate the index from 0 to 2 while the second item is 6914e456bee15e56e7cc09b62591613f0a794f077e3Selim Cinek // translating in. 6924e456bee15e56e7cc09b62591613f0a794f077e3Selim Cinek stackIndex -= 1.0f; 6934e456bee15e56e7cc09b62591613f0a794f077e3Selim Cinek if (algorithmState.scrollY > mCollapsedSize) { 6944e456bee15e56e7cc09b62591613f0a794f077e3Selim Cinek 6954e456bee15e56e7cc09b62591613f0a794f077e3Selim Cinek // Since there is a shadow treshhold, we cant just interpolate from 0 to 6964e456bee15e56e7cc09b62591613f0a794f077e3Selim Cinek // 2 but we interpolate from 0.1f to 2.0f when scrolled in. The jump in 6974e456bee15e56e7cc09b62591613f0a794f077e3Selim Cinek // height will not be noticable since we have padding in between. 6984e456bee15e56e7cc09b62591613f0a794f077e3Selim Cinek stackIndex = 0.1f + stackIndex * 1.9f; 6994e456bee15e56e7cc09b62591613f0a794f077e3Selim Cinek } 7004e456bee15e56e7cc09b62591613f0a794f077e3Selim Cinek } 70167b2260093774f5866f781aede52830440f4ed0eSelim Cinek childViewState.zTranslation = mZBasicHeight 70267b2260093774f5866f781aede52830440f4ed0eSelim Cinek + stackIndex * mZDistanceBetweenElements; 70367b2260093774f5866f781aede52830440f4ed0eSelim Cinek } else if (i > (childCount - 1 - algorithmState.itemsInBottomStack)) { 70467b2260093774f5866f781aede52830440f4ed0eSelim Cinek float numItemsAbove = i - (childCount - 1 - algorithmState.itemsInBottomStack); 70567b2260093774f5866f781aede52830440f4ed0eSelim Cinek float translationZ = mZBasicHeight 70667b2260093774f5866f781aede52830440f4ed0eSelim Cinek - numItemsAbove * mZDistanceBetweenElements; 70767b2260093774f5866f781aede52830440f4ed0eSelim Cinek childViewState.zTranslation = translationZ; 70867b2260093774f5866f781aede52830440f4ed0eSelim Cinek } else { 70967b2260093774f5866f781aede52830440f4ed0eSelim Cinek childViewState.zTranslation = mZBasicHeight; 71067b2260093774f5866f781aede52830440f4ed0eSelim Cinek } 71167b2260093774f5866f781aede52830440f4ed0eSelim Cinek } 71267b2260093774f5866f781aede52830440f4ed0eSelim Cinek } 71367b2260093774f5866f781aede52830440f4ed0eSelim Cinek 714343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek public void setLayoutHeight(int layoutHeight) { 71567b2260093774f5866f781aede52830440f4ed0eSelim Cinek this.mLayoutHeight = layoutHeight; 7168c1a44b62f82c956cbe4aa0809cbdf255d0fae1fJorim Jaggi updateInnerHeight(); 7178c1a44b62f82c956cbe4aa0809cbdf255d0fae1fJorim Jaggi } 7188c1a44b62f82c956cbe4aa0809cbdf255d0fae1fJorim Jaggi 7198c1a44b62f82c956cbe4aa0809cbdf255d0fae1fJorim Jaggi public void setTopPadding(int topPadding) { 7208c1a44b62f82c956cbe4aa0809cbdf255d0fae1fJorim Jaggi mTopPadding = topPadding; 7218c1a44b62f82c956cbe4aa0809cbdf255d0fae1fJorim Jaggi updateInnerHeight(); 7228c1a44b62f82c956cbe4aa0809cbdf255d0fae1fJorim Jaggi } 7238c1a44b62f82c956cbe4aa0809cbdf255d0fae1fJorim Jaggi 7248c1a44b62f82c956cbe4aa0809cbdf255d0fae1fJorim Jaggi private void updateInnerHeight() { 7258c1a44b62f82c956cbe4aa0809cbdf255d0fae1fJorim Jaggi mInnerHeight = mLayoutHeight - mTopPadding; 72667b2260093774f5866f781aede52830440f4ed0eSelim Cinek } 72767b2260093774f5866f781aede52830440f4ed0eSelim Cinek 7283afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek 7293afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek /** 7303afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek * Update whether the device is very small, i.e. Notifications can be in both the top and the 7313afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek * bottom stack at the same time 7323afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek * 7333afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek * @param panelHeight The normal height of the panel when it's open 7343afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek */ 7353afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek public void updateIsSmallScreen(int panelHeight) { 7363afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek mIsSmallScreen = panelHeight < 7373afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek mCollapsedSize /* top stack */ 7383afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek + mBottomStackSlowDownLength + mBottomStackPeekSize /* bottom stack */ 7393afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek + mMaxNotificationHeight; /* max notification height */ 7403afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek } 7413afd00e9f2b55a21ca378d6e520d24283dbb62e0Selim Cinek 7421685e634fb0b14033bd436af8d7174436699ffecSelim Cinek public void onExpansionStarted(StackScrollState currentState) { 7431685e634fb0b14033bd436af8d7174436699ffecSelim Cinek mIsExpansionChanging = true; 7441685e634fb0b14033bd436af8d7174436699ffecSelim Cinek mExpandedOnStart = mIsExpanded; 7451685e634fb0b14033bd436af8d7174436699ffecSelim Cinek ViewGroup hostView = currentState.getHostView(); 7461685e634fb0b14033bd436af8d7174436699ffecSelim Cinek updateFirstChildHeightWhileExpanding(hostView); 7471685e634fb0b14033bd436af8d7174436699ffecSelim Cinek } 7481685e634fb0b14033bd436af8d7174436699ffecSelim Cinek 7491685e634fb0b14033bd436af8d7174436699ffecSelim Cinek private void updateFirstChildHeightWhileExpanding(ViewGroup hostView) { 750be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi mFirstChildWhileExpanding = (ExpandableView) findFirstVisibleChild(hostView); 7519f347ae27c9c9051f5130ac27fffb0e4fbef01a3Jorim Jaggi if (mFirstChildWhileExpanding != null) { 7521685e634fb0b14033bd436af8d7174436699ffecSelim Cinek if (mExpandedOnStart) { 7531685e634fb0b14033bd436af8d7174436699ffecSelim Cinek 7541685e634fb0b14033bd436af8d7174436699ffecSelim Cinek // We are collapsing the shade, so the first child can get as most as high as the 75502af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek // current height or the end value of the animation. 75602af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek mFirstChildMaxHeight = StackStateAnimator.getFinalActualHeight( 75702af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek mFirstChildWhileExpanding); 7581685e634fb0b14033bd436af8d7174436699ffecSelim Cinek } else { 75931094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek updateFirstChildMaxSizeToMaxHeight(); 7601685e634fb0b14033bd436af8d7174436699ffecSelim Cinek } 7611685e634fb0b14033bd436af8d7174436699ffecSelim Cinek } else { 7621685e634fb0b14033bd436af8d7174436699ffecSelim Cinek mFirstChildMaxHeight = 0; 7631685e634fb0b14033bd436af8d7174436699ffecSelim Cinek } 7641685e634fb0b14033bd436af8d7174436699ffecSelim Cinek } 7651685e634fb0b14033bd436af8d7174436699ffecSelim Cinek 76631094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek private void updateFirstChildMaxSizeToMaxHeight() { 76731094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek // We are expanding the shade, expand it to its full height. 76831094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek if (!isMaxSizeInitialized(mFirstChildWhileExpanding)) { 76931094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek 77031094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek // This child was not layouted yet, wait for a layout pass 77131094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek mFirstChildWhileExpanding 77231094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek .addOnLayoutChangeListener(new View.OnLayoutChangeListener() { 77331094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek @Override 77431094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek public void onLayoutChange(View v, int left, int top, int right, 77531094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek int bottom, int oldLeft, int oldTop, int oldRight, 77631094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek int oldBottom) { 77731094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek if (mFirstChildWhileExpanding != null) { 77831094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek mFirstChildMaxHeight = getMaxAllowedChildHeight( 77931094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek mFirstChildWhileExpanding); 78031094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek } else { 78131094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek mFirstChildMaxHeight = 0; 78231094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek } 78331094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek v.removeOnLayoutChangeListener(this); 78431094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek } 78531094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek }); 78631094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek } else { 78731094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek mFirstChildMaxHeight = getMaxAllowedChildHeight(mFirstChildWhileExpanding); 78831094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek } 78931094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek } 79031094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek 7917d447726e2cb9fae80db417012039828daab8fe7Selim Cinek private boolean isMaxSizeInitialized(ExpandableView child) { 7927d447726e2cb9fae80db417012039828daab8fe7Selim Cinek if (child instanceof ExpandableNotificationRow) { 7937d447726e2cb9fae80db417012039828daab8fe7Selim Cinek ExpandableNotificationRow row = (ExpandableNotificationRow) child; 79431094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek return row.isMaxExpandHeightInitialized(); 7957d447726e2cb9fae80db417012039828daab8fe7Selim Cinek } 7967d447726e2cb9fae80db417012039828daab8fe7Selim Cinek return child == null || child.getWidth() != 0; 7977d447726e2cb9fae80db417012039828daab8fe7Selim Cinek } 7987d447726e2cb9fae80db417012039828daab8fe7Selim Cinek 7999f347ae27c9c9051f5130ac27fffb0e4fbef01a3Jorim Jaggi private View findFirstVisibleChild(ViewGroup container) { 8009f347ae27c9c9051f5130ac27fffb0e4fbef01a3Jorim Jaggi int childCount = container.getChildCount(); 8019f347ae27c9c9051f5130ac27fffb0e4fbef01a3Jorim Jaggi for (int i = 0; i < childCount; i++) { 8029f347ae27c9c9051f5130ac27fffb0e4fbef01a3Jorim Jaggi View child = container.getChildAt(i); 8039f347ae27c9c9051f5130ac27fffb0e4fbef01a3Jorim Jaggi if (child.getVisibility() != View.GONE) { 8049f347ae27c9c9051f5130ac27fffb0e4fbef01a3Jorim Jaggi return child; 8059f347ae27c9c9051f5130ac27fffb0e4fbef01a3Jorim Jaggi } 8069f347ae27c9c9051f5130ac27fffb0e4fbef01a3Jorim Jaggi } 8079f347ae27c9c9051f5130ac27fffb0e4fbef01a3Jorim Jaggi return null; 8089f347ae27c9c9051f5130ac27fffb0e4fbef01a3Jorim Jaggi } 8099f347ae27c9c9051f5130ac27fffb0e4fbef01a3Jorim Jaggi 8101685e634fb0b14033bd436af8d7174436699ffecSelim Cinek public void onExpansionStopped() { 8111685e634fb0b14033bd436af8d7174436699ffecSelim Cinek mIsExpansionChanging = false; 8121685e634fb0b14033bd436af8d7174436699ffecSelim Cinek mFirstChildWhileExpanding = null; 8131685e634fb0b14033bd436af8d7174436699ffecSelim Cinek } 8141685e634fb0b14033bd436af8d7174436699ffecSelim Cinek 8151685e634fb0b14033bd436af8d7174436699ffecSelim Cinek public void setIsExpanded(boolean isExpanded) { 8161685e634fb0b14033bd436af8d7174436699ffecSelim Cinek this.mIsExpanded = isExpanded; 8171685e634fb0b14033bd436af8d7174436699ffecSelim Cinek } 8181685e634fb0b14033bd436af8d7174436699ffecSelim Cinek 81902af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek public void notifyChildrenChanged(final ViewGroup hostView) { 8201685e634fb0b14033bd436af8d7174436699ffecSelim Cinek if (mIsExpansionChanging) { 82102af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek hostView.post(new Runnable() { 82202af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek @Override 82302af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek public void run() { 82402af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek updateFirstChildHeightWhileExpanding(hostView); 82502af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek } 82602af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek }); 8271685e634fb0b14033bd436af8d7174436699ffecSelim Cinek } 8281685e634fb0b14033bd436af8d7174436699ffecSelim Cinek } 8291685e634fb0b14033bd436af8d7174436699ffecSelim Cinek 83034c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek public void setDimmed(boolean dimmed) { 83134c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek updatePadding(dimmed); 83234c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek } 83334c0a8d72aee1867cf7b6d04531c7faec76ab473Selim Cinek 83431094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek public void onReset(ExpandableView view) { 83531094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek if (view.equals(mFirstChildWhileExpanding)) { 83631094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek updateFirstChildMaxSizeToMaxHeight(); 83731094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek } 83831094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek } 83931094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek 84067b2260093774f5866f781aede52830440f4ed0eSelim Cinek class StackScrollAlgorithmState { 84167b2260093774f5866f781aede52830440f4ed0eSelim Cinek 84267b2260093774f5866f781aede52830440f4ed0eSelim Cinek /** 84367b2260093774f5866f781aede52830440f4ed0eSelim Cinek * The scroll position of the algorithm 84467b2260093774f5866f781aede52830440f4ed0eSelim Cinek */ 84567b2260093774f5866f781aede52830440f4ed0eSelim Cinek public int scrollY; 84667b2260093774f5866f781aede52830440f4ed0eSelim Cinek 84767b2260093774f5866f781aede52830440f4ed0eSelim Cinek /** 84867b2260093774f5866f781aede52830440f4ed0eSelim Cinek * The quantity of items which are in the top stack. 84967b2260093774f5866f781aede52830440f4ed0eSelim Cinek */ 85067b2260093774f5866f781aede52830440f4ed0eSelim Cinek public float itemsInTopStack; 85167b2260093774f5866f781aede52830440f4ed0eSelim Cinek 85267b2260093774f5866f781aede52830440f4ed0eSelim Cinek /** 85367b2260093774f5866f781aede52830440f4ed0eSelim Cinek * how far in is the element currently transitioning into the top stack 85467b2260093774f5866f781aede52830440f4ed0eSelim Cinek */ 85567b2260093774f5866f781aede52830440f4ed0eSelim Cinek public float partialInTop; 85667b2260093774f5866f781aede52830440f4ed0eSelim Cinek 85767b2260093774f5866f781aede52830440f4ed0eSelim Cinek /** 858343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek * The number of pixels the last child in the top stack has scrolled in to the stack 859343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek */ 860343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek public float scrolledPixelsTop; 861343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek 862343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek /** 86367b2260093774f5866f781aede52830440f4ed0eSelim Cinek * The last item index which is in the top stack. 86467b2260093774f5866f781aede52830440f4ed0eSelim Cinek */ 86567b2260093774f5866f781aede52830440f4ed0eSelim Cinek public int lastTopStackIndex; 86667b2260093774f5866f781aede52830440f4ed0eSelim Cinek 86767b2260093774f5866f781aede52830440f4ed0eSelim Cinek /** 86867b2260093774f5866f781aede52830440f4ed0eSelim Cinek * The quantity of items which are in the bottom stack. 86967b2260093774f5866f781aede52830440f4ed0eSelim Cinek */ 87067b2260093774f5866f781aede52830440f4ed0eSelim Cinek public float itemsInBottomStack; 87167b2260093774f5866f781aede52830440f4ed0eSelim Cinek 87267b2260093774f5866f781aede52830440f4ed0eSelim Cinek /** 87367b2260093774f5866f781aede52830440f4ed0eSelim Cinek * how far in is the element currently transitioning into the bottom stack 87467b2260093774f5866f781aede52830440f4ed0eSelim Cinek */ 87567b2260093774f5866f781aede52830440f4ed0eSelim Cinek public float partialInBottom; 876d4a57440ca5fc8461959176475b0fcd8a6e05871Jorim Jaggi 877d4a57440ca5fc8461959176475b0fcd8a6e05871Jorim Jaggi /** 878d4a57440ca5fc8461959176475b0fcd8a6e05871Jorim Jaggi * The children from the host view which are not gone. 879d4a57440ca5fc8461959176475b0fcd8a6e05871Jorim Jaggi */ 880be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi public final ArrayList<ExpandableView> visibleChildren = new ArrayList<ExpandableView>(); 88167b2260093774f5866f781aede52830440f4ed0eSelim Cinek } 88267b2260093774f5866f781aede52830440f4ed0eSelim Cinek 88367b2260093774f5866f781aede52830440f4ed0eSelim Cinek} 884