1be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi/* 2be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * Copyright (C) 2014 The Android Open Source Project 3be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * 4be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * Licensed under the Apache License, Version 2.0 (the "License"); 5be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * you may not use this file except in compliance with the License. 6be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * You may obtain a copy of the License at 7be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * 8be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * http://www.apache.org/licenses/LICENSE-2.0 9be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * 10be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * Unless required by applicable law or agreed to in writing, software 11be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * distributed under the License is distributed on an "AS IS" BASIS, 12be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * See the License for the specific language governing permissions and 14be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * limitations under the License 15be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi */ 16be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi 17be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggipackage com.android.systemui.statusbar; 18be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi 19be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggiimport android.content.Context; 20e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinekimport android.graphics.Rect; 21be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggiimport android.util.AttributeSet; 2200ebdfe8ba98c05a767660de2ed7c9a19fb49d74Jorim Jaggiimport android.view.MotionEvent; 23be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggiimport android.view.View; 24c9c00ae2fa5fb787e9f12705f8cd8de445ecde4bSelim Cinekimport android.view.ViewGroup; 2524d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinekimport android.widget.FrameLayout; 2624d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinekimport com.android.systemui.R; 27e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinekimport com.android.systemui.statusbar.stack.NotificationStackScrollLayout; 2824d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek 2924d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinekimport java.util.ArrayList; 30be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi 31be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi/** 32be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * An abstract view for expandable views. 33be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi */ 3424d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinekpublic abstract class ExpandableView extends FrameLayout { 3524d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek 36b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek private final int mBottomDecorHeight; 37b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek protected OnHeightChangedListener mOnHeightChangedListener; 38379ff8f6b18b236b679a59a2dc14c0baeede3baeSelim Cinek protected int mMaxViewHeight; 39310df3127aace5a82cdc107fdb1e2d6957f38bccChris Wren private int mActualHeight; 40be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi protected int mClipTopAmount; 41be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi private boolean mActualHeightInitialized; 424e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi private boolean mDark; 4324d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek private ArrayList<View> mMatchParentViews = new ArrayList<View>(); 44a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek private int mClipTopOptimization; 45a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek private static Rect mClipRect = new Rect(); 462cd45dfba6a9105a305ea20b110ba5ac078a9dc6Selim Cinek private boolean mWillBeGone; 479c17b7749377a047794157bc066e45d985cabf52Selim Cinek private int mMinClipTopAmount = 0; 48be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi 49be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi public ExpandableView(Context context, AttributeSet attrs) { 50be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi super(context, attrs); 51379ff8f6b18b236b679a59a2dc14c0baeede3baeSelim Cinek mMaxViewHeight = getResources().getDimensionPixelSize( 5224d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek R.dimen.notification_max_height); 53b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek mBottomDecorHeight = resolveBottomDecorHeight(); 54b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek } 55b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek 56b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek protected int resolveBottomDecorHeight() { 57b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek return getResources().getDimensionPixelSize( 58b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek R.dimen.notification_bottom_decor_height); 59be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi } 60be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi 61be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi @Override 6224d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 63379ff8f6b18b236b679a59a2dc14c0baeede3baeSelim Cinek int ownMaxHeight = mMaxViewHeight; 646f145cf0c02fdbd324ff2d62a86f19bff5eb7ebeSelim Cinek int heightMode = MeasureSpec.getMode(heightMeasureSpec); 656f145cf0c02fdbd324ff2d62a86f19bff5eb7ebeSelim Cinek boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY; 666f145cf0c02fdbd324ff2d62a86f19bff5eb7ebeSelim Cinek if (hasFixedHeight) { 676f145cf0c02fdbd324ff2d62a86f19bff5eb7ebeSelim Cinek // We have a height set in our layout, so we want to be at most as big as given 686f145cf0c02fdbd324ff2d62a86f19bff5eb7ebeSelim Cinek ownMaxHeight = Math.min(MeasureSpec.getSize(heightMeasureSpec), ownMaxHeight); 696f145cf0c02fdbd324ff2d62a86f19bff5eb7ebeSelim Cinek } 7024d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek int newHeightSpec = MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.AT_MOST); 7124d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek int maxChildHeight = 0; 7224d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek int childCount = getChildCount(); 7324d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek for (int i = 0; i < childCount; i++) { 74c9c00ae2fa5fb787e9f12705f8cd8de445ecde4bSelim Cinek View child = getChildAt(i); 75b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek if (child.getVisibility() == GONE || isChildInvisible(child)) { 76b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek continue; 77b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek } 7824d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek int childHeightSpec = newHeightSpec; 7924d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek ViewGroup.LayoutParams layoutParams = child.getLayoutParams(); 8024d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek if (layoutParams.height != ViewGroup.LayoutParams.MATCH_PARENT) { 8124d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek if (layoutParams.height >= 0) { 8224d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek // An actual height is set 8324d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek childHeightSpec = layoutParams.height > ownMaxHeight 8424d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek ? MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.EXACTLY) 8524d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek : MeasureSpec.makeMeasureSpec(layoutParams.height, MeasureSpec.EXACTLY); 8624d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek } 87b741f053394b6e8f59bdf72bb47e9f4484fbb808Jorim Jaggi child.measure( 88b741f053394b6e8f59bdf72bb47e9f4484fbb808Jorim Jaggi getChildMeasureSpec(widthMeasureSpec, 0 /* padding */, layoutParams.width), 89b741f053394b6e8f59bdf72bb47e9f4484fbb808Jorim Jaggi childHeightSpec); 9024d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek int childHeight = child.getMeasuredHeight(); 9124d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek maxChildHeight = Math.max(maxChildHeight, childHeight); 9224d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek } else { 9324d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek mMatchParentViews.add(child); 9424d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek } 9524d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek } 966f145cf0c02fdbd324ff2d62a86f19bff5eb7ebeSelim Cinek int ownHeight = hasFixedHeight ? ownMaxHeight : Math.min(ownMaxHeight, maxChildHeight); 9724d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek newHeightSpec = MeasureSpec.makeMeasureSpec(ownHeight, MeasureSpec.EXACTLY); 9824d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek for (View child : mMatchParentViews) { 99b741f053394b6e8f59bdf72bb47e9f4484fbb808Jorim Jaggi child.measure(getChildMeasureSpec( 100b741f053394b6e8f59bdf72bb47e9f4484fbb808Jorim Jaggi widthMeasureSpec, 0 /* padding */, child.getLayoutParams().width), 101b741f053394b6e8f59bdf72bb47e9f4484fbb808Jorim Jaggi newHeightSpec); 102c9c00ae2fa5fb787e9f12705f8cd8de445ecde4bSelim Cinek } 10324d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek mMatchParentViews.clear(); 10424d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek int width = MeasureSpec.getSize(widthMeasureSpec); 105b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek if (canHaveBottomDecor()) { 106b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek // We always account for the expandAction as well. 107b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek ownHeight += mBottomDecorHeight; 108b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek } 10924d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek setMeasuredDimension(width, ownHeight); 11024d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek } 11124d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek 11224d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek @Override 11324d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 11424d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek super.onLayout(changed, left, top, right, bottom); 115be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi if (!mActualHeightInitialized && mActualHeight == 0) { 116d2319fbe6a53ac4c38ca02e4d8e32da86ed0994bSelim Cinek int initialHeight = getInitialHeight(); 117d2319fbe6a53ac4c38ca02e4d8e32da86ed0994bSelim Cinek if (initialHeight != 0) { 118b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek setContentHeight(initialHeight); 119d2319fbe6a53ac4c38ca02e4d8e32da86ed0994bSelim Cinek } 120be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi } 121ba67acff25950a32476273b5d60192a3874c2130Selim Cinek updateClipping(); 122be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi } 123be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi 1241a521f3ea841f6db9686bbec7f950a3883d075aaSelim Cinek /** 1251a521f3ea841f6db9686bbec7f950a3883d075aaSelim Cinek * Resets the height of the view on the next layout pass 1261a521f3ea841f6db9686bbec7f950a3883d075aaSelim Cinek */ 1271a521f3ea841f6db9686bbec7f950a3883d075aaSelim Cinek protected void resetActualHeight() { 128310df3127aace5a82cdc107fdb1e2d6957f38bccChris Wren mActualHeight = 0; 129310df3127aace5a82cdc107fdb1e2d6957f38bccChris Wren mActualHeightInitialized = false; 1301a521f3ea841f6db9686bbec7f950a3883d075aaSelim Cinek requestLayout(); 131310df3127aace5a82cdc107fdb1e2d6957f38bccChris Wren } 132310df3127aace5a82cdc107fdb1e2d6957f38bccChris Wren 133c27437b7fd04e682ae2abdf0727a99bf5c6e409dSelim Cinek protected int getInitialHeight() { 134c27437b7fd04e682ae2abdf0727a99bf5c6e409dSelim Cinek return getHeight(); 135c27437b7fd04e682ae2abdf0727a99bf5c6e409dSelim Cinek } 136c27437b7fd04e682ae2abdf0727a99bf5c6e409dSelim Cinek 13700ebdfe8ba98c05a767660de2ed7c9a19fb49d74Jorim Jaggi @Override 138e0c1890bdc5482a4d29aba683115a7133ab58950Adrian Roos public boolean dispatchGenericMotionEvent(MotionEvent ev) { 139e0c1890bdc5482a4d29aba683115a7133ab58950Adrian Roos if (filterMotionEvent(ev)) { 140e0c1890bdc5482a4d29aba683115a7133ab58950Adrian Roos return super.dispatchGenericMotionEvent(ev); 141e0c1890bdc5482a4d29aba683115a7133ab58950Adrian Roos } 142e0c1890bdc5482a4d29aba683115a7133ab58950Adrian Roos return false; 143e0c1890bdc5482a4d29aba683115a7133ab58950Adrian Roos } 144e0c1890bdc5482a4d29aba683115a7133ab58950Adrian Roos 145e0c1890bdc5482a4d29aba683115a7133ab58950Adrian Roos @Override 14600ebdfe8ba98c05a767660de2ed7c9a19fb49d74Jorim Jaggi public boolean dispatchTouchEvent(MotionEvent ev) { 14700ebdfe8ba98c05a767660de2ed7c9a19fb49d74Jorim Jaggi if (filterMotionEvent(ev)) { 14800ebdfe8ba98c05a767660de2ed7c9a19fb49d74Jorim Jaggi return super.dispatchTouchEvent(ev); 14900ebdfe8ba98c05a767660de2ed7c9a19fb49d74Jorim Jaggi } 15000ebdfe8ba98c05a767660de2ed7c9a19fb49d74Jorim Jaggi return false; 15100ebdfe8ba98c05a767660de2ed7c9a19fb49d74Jorim Jaggi } 15200ebdfe8ba98c05a767660de2ed7c9a19fb49d74Jorim Jaggi 1531a521f3ea841f6db9686bbec7f950a3883d075aaSelim Cinek protected boolean filterMotionEvent(MotionEvent event) { 15400ebdfe8ba98c05a767660de2ed7c9a19fb49d74Jorim Jaggi return event.getActionMasked() != MotionEvent.ACTION_DOWN 155e0c1890bdc5482a4d29aba683115a7133ab58950Adrian Roos && event.getActionMasked() != MotionEvent.ACTION_HOVER_ENTER 156e0c1890bdc5482a4d29aba683115a7133ab58950Adrian Roos && event.getActionMasked() != MotionEvent.ACTION_HOVER_MOVE 15700ebdfe8ba98c05a767660de2ed7c9a19fb49d74Jorim Jaggi || event.getY() > mClipTopAmount && event.getY() < mActualHeight; 15800ebdfe8ba98c05a767660de2ed7c9a19fb49d74Jorim Jaggi } 15900ebdfe8ba98c05a767660de2ed7c9a19fb49d74Jorim Jaggi 160be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi /** 161be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * Sets the actual height of this notification. This is different than the laid out 162be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * {@link View#getHeight()}, as we want to avoid layouting during scrolling and expanding. 163d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi * 164d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi * @param actualHeight The height of this notification. 165d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi * @param notifyListeners Whether the listener should be informed about the change. 166be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi */ 167d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi public void setActualHeight(int actualHeight, boolean notifyListeners) { 1682580a976ec93a01ed00fae51364ad872bc591d95Jorim Jaggi mActualHeightInitialized = true; 169be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi mActualHeight = actualHeight; 170a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek updateClipping(); 171d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi if (notifyListeners) { 172b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek notifyHeightChanged(false /* needsAnimation */); 173d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 174d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 175d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi 176b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek public void setContentHeight(int contentHeight) { 177b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek setActualHeight(contentHeight + getBottomDecorHeight(), true); 178be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi } 179be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi 180be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi /** 181be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * See {@link #setActualHeight}. 182be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * 1839cbadd3c08a7d7dd3412743dd04aecb16c5a1595Jorim Jaggi * @return The current actual height of this notification. 184be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi */ 185be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi public int getActualHeight() { 186be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi return mActualHeight; 187be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi } 188be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi 189be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi /** 190b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek * This view may have a bottom decor which will be placed below the content. If it has one, this 191b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek * view will be layouted higher than just the content by {@link #mBottomDecorHeight}. 192b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek * @return the height of the decor if it currently has one 193b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek */ 194b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek public int getBottomDecorHeight() { 195b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek return hasBottomDecor() ? mBottomDecorHeight : 0; 196b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek } 197b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek 198b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek /** 199b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek * @return whether this view may have a bottom decor at all. This will force the view to layout 200b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek * itself higher than just it's content 201b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek */ 202b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek protected boolean canHaveBottomDecor() { 203b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek return false; 204b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek } 205b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek 206b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek /** 207b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek * @return whether this view has a decor view below it's content. This will make the intrinsic 208b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek * height from {@link #getIntrinsicHeight()} higher as well 209b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek */ 210b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek protected boolean hasBottomDecor() { 211b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek return false; 212b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek } 213b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek 214b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek /** 215be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * @return The maximum height of this notification. 216be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi */ 217b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek public int getMaxContentHeight() { 2184222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi return getHeight(); 2194222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi } 2204222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi 2214222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi /** 222b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek * @return The minimum content height of this notification. 2234222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi */ 2244222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi public int getMinHeight() { 2254222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi return getHeight(); 2264222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi } 227be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi 228be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi /** 229d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi * Sets the notification as dimmed. The default implementation does nothing. 230d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi * 231d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi * @param dimmed Whether the notification should be dimmed. 232d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi * @param fade Whether an animation should be played to change the state. 233d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi */ 234d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi public void setDimmed(boolean dimmed, boolean fade) { 235d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 236d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi 237d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi /** 238bf370992508c55d1f2493923bdc1834a0710e4baJohn Spurlock * Sets the notification as dark. The default implementation does nothing. 239bf370992508c55d1f2493923bdc1834a0710e4baJohn Spurlock * 240bf370992508c55d1f2493923bdc1834a0710e4baJohn Spurlock * @param dark Whether the notification should be dark. 241bf370992508c55d1f2493923bdc1834a0710e4baJohn Spurlock * @param fade Whether an animation should be played to change the state. 2424e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi * @param delay If fading, the delay of the animation. 243bf370992508c55d1f2493923bdc1834a0710e4baJohn Spurlock */ 2444e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi public void setDark(boolean dark, boolean fade, long delay) { 2454e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi mDark = dark; 2464e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi } 2474e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi 2484e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi public boolean isDark() { 2494e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi return mDark; 250bf370992508c55d1f2493923bdc1834a0710e4baJohn Spurlock } 251bf370992508c55d1f2493923bdc1834a0710e4baJohn Spurlock 252bf370992508c55d1f2493923bdc1834a0710e4baJohn Spurlock /** 253ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi * See {@link #setHideSensitive}. This is a variant which notifies this view in advance about 254ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi * the upcoming state of hiding sensitive notifications. It gets called at the very beginning 255ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi * of a stack scroller update such that the updated intrinsic height (which is dependent on 256ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi * whether private or public layout is showing) gets taken into account into all layout 257ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi * calculations. 258ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi */ 259ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi public void setHideSensitiveForIntrinsicHeight(boolean hideSensitive) { 260ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi } 261ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi 262ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi /** 263ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi * Sets whether the notification should hide its private contents if it is sensitive. 264ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi */ 265ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi public void setHideSensitive(boolean hideSensitive, boolean animated, long delay, 266ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi long duration) { 267ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi } 268ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi 269ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi /** 2709cbadd3c08a7d7dd3412743dd04aecb16c5a1595Jorim Jaggi * @return The desired notification height. 2719cbadd3c08a7d7dd3412743dd04aecb16c5a1595Jorim Jaggi */ 2729cbadd3c08a7d7dd3412743dd04aecb16c5a1595Jorim Jaggi public int getIntrinsicHeight() { 273a5eaa6034dd48fab0f5a232c09ebed35f359963eSelim Cinek return getHeight(); 2749cbadd3c08a7d7dd3412743dd04aecb16c5a1595Jorim Jaggi } 2759cbadd3c08a7d7dd3412743dd04aecb16c5a1595Jorim Jaggi 2769cbadd3c08a7d7dd3412743dd04aecb16c5a1595Jorim Jaggi /** 277be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * Sets the amount this view should be clipped from the top. This is used when an expanded 278be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * notification is scrolling in the top or bottom stack. 279be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * 280be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * @param clipTopAmount The amount of pixels this view should be clipped from top. 281be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi */ 282be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi public void setClipTopAmount(int clipTopAmount) { 283be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi mClipTopAmount = clipTopAmount; 284be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi } 285be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi 286eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public int getClipTopAmount() { 287eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return mClipTopAmount; 288eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 289eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 290be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi public void setOnHeightChangedListener(OnHeightChangedListener listener) { 291be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi mOnHeightChangedListener = listener; 292be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi } 293be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi 294be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi /** 2954222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi * @return Whether we can expand this views content. 296be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi */ 2974222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi public boolean isContentExpandable() { 2984222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi return false; 299be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi } 300be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi 301b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek public void notifyHeightChanged(boolean needsAnimation) { 3029cbadd3c08a7d7dd3412743dd04aecb16c5a1595Jorim Jaggi if (mOnHeightChangedListener != null) { 303b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek mOnHeightChangedListener.onHeightChanged(this, needsAnimation); 3049cbadd3c08a7d7dd3412743dd04aecb16c5a1595Jorim Jaggi } 3059cbadd3c08a7d7dd3412743dd04aecb16c5a1595Jorim Jaggi } 3069cbadd3c08a7d7dd3412743dd04aecb16c5a1595Jorim Jaggi 307c27437b7fd04e682ae2abdf0727a99bf5c6e409dSelim Cinek public boolean isTransparent() { 308c27437b7fd04e682ae2abdf0727a99bf5c6e409dSelim Cinek return false; 309c27437b7fd04e682ae2abdf0727a99bf5c6e409dSelim Cinek } 310c27437b7fd04e682ae2abdf0727a99bf5c6e409dSelim Cinek 311be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi /** 3128efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek * Perform a remove animation on this view. 3138efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek * 31460d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi * @param duration The duration of the remove animation. 3158efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek * @param translationDirection The direction value from [-1 ... 1] indicating in which the 3168efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek * animation should be performed. A value of -1 means that The 3178efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek * remove animation should be performed upwards, 3188efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek * such that the child appears to be going away to the top. 1 3198efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek * Should mean the opposite. 3208efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek * @param onFinishedRunnable A runnable which should be run when the animation is finished. 3218efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek */ 32260d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi public abstract void performRemoveAnimation(long duration, float translationDirection, 3238efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek Runnable onFinishedRunnable); 3248efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 32560d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi public abstract void performAddAnimation(long delay, long duration); 3268efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 3273d2b94bf8e32640e57573ebb17911b1db9440231Selim Cinek public void setBelowSpeedBump(boolean below) { 3283d2b94bf8e32640e57573ebb17911b1db9440231Selim Cinek } 3293d2b94bf8e32640e57573ebb17911b1db9440231Selim Cinek 33031094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek public void onHeightReset() { 331e34c6513bb1a4d3e246866c2a7f0619914f18bd3Selim Cinek if (mOnHeightChangedListener != null) { 332e34c6513bb1a4d3e246866c2a7f0619914f18bd3Selim Cinek mOnHeightChangedListener.onReset(this); 333e34c6513bb1a4d3e246866c2a7f0619914f18bd3Selim Cinek } 334a5e211b1f2a8d055b369dadc464dc5d5bc3dd9c1Selim Cinek } 335a5e211b1f2a8d055b369dadc464dc5d5bc3dd9c1Selim Cinek 3368efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek /** 337e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek * This method returns the drawing rect for the view which is different from the regular 338e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek * drawing rect, since we layout all children in the {@link NotificationStackScrollLayout} at 339e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek * position 0 and usually the translation is neglected. Since we are manually clipping this 340e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek * view,we also need to subtract the clipTopAmount from the top. This is needed in order to 341e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek * ensure that accessibility and focusing work correctly. 342e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek * 343e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek * @param outRect The (scrolled) drawing bounds of the view. 344e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek */ 345e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek @Override 346e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek public void getDrawingRect(Rect outRect) { 347e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek super.getDrawingRect(outRect); 348e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek outRect.left += getTranslationX(); 349e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek outRect.right += getTranslationX(); 350e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek outRect.bottom = (int) (outRect.top + getTranslationY() + getActualHeight()); 351e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek outRect.top += getTranslationY() + getClipTopAmount(); 352e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek } 353e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek 354cd55f43db60c2f641252d829bd3f532358625834Adrian Roos @Override 355cd55f43db60c2f641252d829bd3f532358625834Adrian Roos public void getBoundsOnScreen(Rect outRect, boolean clipToParent) { 356cd55f43db60c2f641252d829bd3f532358625834Adrian Roos super.getBoundsOnScreen(outRect, clipToParent); 357b44f5486ef7c2e169d5a65eb159a1d842d9c3d51Adrian Roos outRect.bottom = outRect.top + getActualHeight(); 358b44f5486ef7c2e169d5a65eb159a1d842d9c3d51Adrian Roos outRect.top += getClipTopOptimization(); 359cd55f43db60c2f641252d829bd3f532358625834Adrian Roos } 360cd55f43db60c2f641252d829bd3f532358625834Adrian Roos 361b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek public int getContentHeight() { 362b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek return mActualHeight - getBottomDecorHeight(); 363b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek } 364b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek 365b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek /** 366b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek * @return whether the given child can be ignored for layouting and measuring purposes 367b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek */ 368b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek protected boolean isChildInvisible(View child) { 369b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek return false; 370b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek } 371b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek 372b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek public boolean areChildrenExpanded() { 373b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek return false; 374b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek } 375b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek 376a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek private void updateClipping() { 377f92a1fdb77311149189ae17244adc51017b89c1eSelim Cinek int top = mClipTopOptimization; 378f92a1fdb77311149189ae17244adc51017b89c1eSelim Cinek if (top >= getActualHeight()) { 379f92a1fdb77311149189ae17244adc51017b89c1eSelim Cinek top = getActualHeight() - 1; 380f92a1fdb77311149189ae17244adc51017b89c1eSelim Cinek } 381f92a1fdb77311149189ae17244adc51017b89c1eSelim Cinek mClipRect.set(0, top, getWidth(), getActualHeight()); 382a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek setClipBounds(mClipRect); 383a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek } 384a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek 385a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek public int getClipTopOptimization() { 386a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek return mClipTopOptimization; 387a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek } 388a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek 389a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek /** 390a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek * Set that the view will be clipped by a given amount from the top. Contrary to 391a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek * {@link #setClipTopAmount} this amount doesn't effect shadows and the background. 392a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek * 393a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek * @param clipTopOptimization the amount to clip from the top 394a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek */ 395a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek public void setClipTopOptimization(int clipTopOptimization) { 396a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek mClipTopOptimization = clipTopOptimization; 397a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek updateClipping(); 398a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek } 399a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek 4002cd45dfba6a9105a305ea20b110ba5ac078a9dc6Selim Cinek public boolean willBeGone() { 4012cd45dfba6a9105a305ea20b110ba5ac078a9dc6Selim Cinek return mWillBeGone; 4022cd45dfba6a9105a305ea20b110ba5ac078a9dc6Selim Cinek } 4032cd45dfba6a9105a305ea20b110ba5ac078a9dc6Selim Cinek 4042cd45dfba6a9105a305ea20b110ba5ac078a9dc6Selim Cinek public void setWillBeGone(boolean willBeGone) { 4052cd45dfba6a9105a305ea20b110ba5ac078a9dc6Selim Cinek mWillBeGone = willBeGone; 4062cd45dfba6a9105a305ea20b110ba5ac078a9dc6Selim Cinek } 4072cd45dfba6a9105a305ea20b110ba5ac078a9dc6Selim Cinek 4089c17b7749377a047794157bc066e45d985cabf52Selim Cinek public int getMinClipTopAmount() { 4099c17b7749377a047794157bc066e45d985cabf52Selim Cinek return mMinClipTopAmount; 4109c17b7749377a047794157bc066e45d985cabf52Selim Cinek } 4119c17b7749377a047794157bc066e45d985cabf52Selim Cinek 4129c17b7749377a047794157bc066e45d985cabf52Selim Cinek public void setMinClipTopAmount(int minClipTopAmount) { 4139c17b7749377a047794157bc066e45d985cabf52Selim Cinek mMinClipTopAmount = minClipTopAmount; 4149c17b7749377a047794157bc066e45d985cabf52Selim Cinek } 4159c17b7749377a047794157bc066e45d985cabf52Selim Cinek 416e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek /** 417be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * A listener notifying when {@link #getActualHeight} changes. 418be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi */ 419be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi public interface OnHeightChangedListener { 42030c305ce6283ce1380ad91ef0d221696b32d5a6bJorim Jaggi 42130c305ce6283ce1380ad91ef0d221696b32d5a6bJorim Jaggi /** 42230c305ce6283ce1380ad91ef0d221696b32d5a6bJorim Jaggi * @param view the view for which the height changed, or {@code null} if just the top 42330c305ce6283ce1380ad91ef0d221696b32d5a6bJorim Jaggi * padding or the padding between the elements changed 424b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek * @param needsAnimation whether the view height needs to be animated 42530c305ce6283ce1380ad91ef0d221696b32d5a6bJorim Jaggi */ 426b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek void onHeightChanged(ExpandableView view, boolean needsAnimation); 427a5e211b1f2a8d055b369dadc464dc5d5bc3dd9c1Selim Cinek 428a5e211b1f2a8d055b369dadc464dc5d5bc3dd9c1Selim Cinek /** 429a5e211b1f2a8d055b369dadc464dc5d5bc3dd9c1Selim Cinek * Called when the view is reset and therefore the height will change abruptly 430a5e211b1f2a8d055b369dadc464dc5d5bc3dd9c1Selim Cinek * 431a5e211b1f2a8d055b369dadc464dc5d5bc3dd9c1Selim Cinek * @param view The view which was reset. 432a5e211b1f2a8d055b369dadc464dc5d5bc3dd9c1Selim Cinek */ 433a5e211b1f2a8d055b369dadc464dc5d5bc3dd9c1Selim Cinek void onReset(ExpandableView view); 434be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi } 435be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi} 436