ExpandableView.java revision 6f145cf0c02fdbd324ff2d62a86f19bff5eb7ebe
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(); 46be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi 47be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi public ExpandableView(Context context, AttributeSet attrs) { 48be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi super(context, attrs); 49379ff8f6b18b236b679a59a2dc14c0baeede3baeSelim Cinek mMaxViewHeight = getResources().getDimensionPixelSize( 5024d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek R.dimen.notification_max_height); 51b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek mBottomDecorHeight = resolveBottomDecorHeight(); 52b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek } 53b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek 54b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek protected int resolveBottomDecorHeight() { 55b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek return getResources().getDimensionPixelSize( 56b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek R.dimen.notification_bottom_decor_height); 57be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi } 58be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi 59be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi @Override 6024d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 61379ff8f6b18b236b679a59a2dc14c0baeede3baeSelim Cinek int ownMaxHeight = mMaxViewHeight; 626f145cf0c02fdbd324ff2d62a86f19bff5eb7ebeSelim Cinek int heightMode = MeasureSpec.getMode(heightMeasureSpec); 636f145cf0c02fdbd324ff2d62a86f19bff5eb7ebeSelim Cinek boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY; 646f145cf0c02fdbd324ff2d62a86f19bff5eb7ebeSelim Cinek if (hasFixedHeight) { 656f145cf0c02fdbd324ff2d62a86f19bff5eb7ebeSelim Cinek // We have a height set in our layout, so we want to be at most as big as given 666f145cf0c02fdbd324ff2d62a86f19bff5eb7ebeSelim Cinek ownMaxHeight = Math.min(MeasureSpec.getSize(heightMeasureSpec), ownMaxHeight); 676f145cf0c02fdbd324ff2d62a86f19bff5eb7ebeSelim Cinek } 6824d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek int newHeightSpec = MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.AT_MOST); 6924d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek int maxChildHeight = 0; 7024d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek int childCount = getChildCount(); 7124d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek for (int i = 0; i < childCount; i++) { 72c9c00ae2fa5fb787e9f12705f8cd8de445ecde4bSelim Cinek View child = getChildAt(i); 73b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek if (child.getVisibility() == GONE || isChildInvisible(child)) { 74b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek continue; 75b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek } 7624d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek int childHeightSpec = newHeightSpec; 7724d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek ViewGroup.LayoutParams layoutParams = child.getLayoutParams(); 7824d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek if (layoutParams.height != ViewGroup.LayoutParams.MATCH_PARENT) { 7924d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek if (layoutParams.height >= 0) { 8024d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek // An actual height is set 8124d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek childHeightSpec = layoutParams.height > ownMaxHeight 8224d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek ? MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.EXACTLY) 8324d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek : MeasureSpec.makeMeasureSpec(layoutParams.height, MeasureSpec.EXACTLY); 8424d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek } 85b741f053394b6e8f59bdf72bb47e9f4484fbb808Jorim Jaggi child.measure( 86b741f053394b6e8f59bdf72bb47e9f4484fbb808Jorim Jaggi getChildMeasureSpec(widthMeasureSpec, 0 /* padding */, layoutParams.width), 87b741f053394b6e8f59bdf72bb47e9f4484fbb808Jorim Jaggi childHeightSpec); 8824d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek int childHeight = child.getMeasuredHeight(); 8924d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek maxChildHeight = Math.max(maxChildHeight, childHeight); 9024d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek } else { 9124d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek mMatchParentViews.add(child); 9224d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek } 9324d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek } 946f145cf0c02fdbd324ff2d62a86f19bff5eb7ebeSelim Cinek int ownHeight = hasFixedHeight ? ownMaxHeight : Math.min(ownMaxHeight, maxChildHeight); 9524d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek newHeightSpec = MeasureSpec.makeMeasureSpec(ownHeight, MeasureSpec.EXACTLY); 9624d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek for (View child : mMatchParentViews) { 97b741f053394b6e8f59bdf72bb47e9f4484fbb808Jorim Jaggi child.measure(getChildMeasureSpec( 98b741f053394b6e8f59bdf72bb47e9f4484fbb808Jorim Jaggi widthMeasureSpec, 0 /* padding */, child.getLayoutParams().width), 99b741f053394b6e8f59bdf72bb47e9f4484fbb808Jorim Jaggi newHeightSpec); 100c9c00ae2fa5fb787e9f12705f8cd8de445ecde4bSelim Cinek } 10124d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek mMatchParentViews.clear(); 10224d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek int width = MeasureSpec.getSize(widthMeasureSpec); 103b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek if (canHaveBottomDecor()) { 104b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek // We always account for the expandAction as well. 105b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek ownHeight += mBottomDecorHeight; 106b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek } 10724d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek setMeasuredDimension(width, ownHeight); 10824d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek } 10924d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek 11024d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek @Override 11124d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 11224d7cfa4d6a3331708bb7b37f551b4f534b02f7cSelim Cinek super.onLayout(changed, left, top, right, bottom); 113be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi if (!mActualHeightInitialized && mActualHeight == 0) { 114d2319fbe6a53ac4c38ca02e4d8e32da86ed0994bSelim Cinek int initialHeight = getInitialHeight(); 115d2319fbe6a53ac4c38ca02e4d8e32da86ed0994bSelim Cinek if (initialHeight != 0) { 116b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek setContentHeight(initialHeight); 117d2319fbe6a53ac4c38ca02e4d8e32da86ed0994bSelim Cinek } 118be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi } 119ba67acff25950a32476273b5d60192a3874c2130Selim Cinek updateClipping(); 120be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi } 121be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi 1221a521f3ea841f6db9686bbec7f950a3883d075aaSelim Cinek /** 1231a521f3ea841f6db9686bbec7f950a3883d075aaSelim Cinek * Resets the height of the view on the next layout pass 1241a521f3ea841f6db9686bbec7f950a3883d075aaSelim Cinek */ 1251a521f3ea841f6db9686bbec7f950a3883d075aaSelim Cinek protected void resetActualHeight() { 126310df3127aace5a82cdc107fdb1e2d6957f38bccChris Wren mActualHeight = 0; 127310df3127aace5a82cdc107fdb1e2d6957f38bccChris Wren mActualHeightInitialized = false; 1281a521f3ea841f6db9686bbec7f950a3883d075aaSelim Cinek requestLayout(); 129310df3127aace5a82cdc107fdb1e2d6957f38bccChris Wren } 130310df3127aace5a82cdc107fdb1e2d6957f38bccChris Wren 131c27437b7fd04e682ae2abdf0727a99bf5c6e409dSelim Cinek protected int getInitialHeight() { 132c27437b7fd04e682ae2abdf0727a99bf5c6e409dSelim Cinek return getHeight(); 133c27437b7fd04e682ae2abdf0727a99bf5c6e409dSelim Cinek } 134c27437b7fd04e682ae2abdf0727a99bf5c6e409dSelim Cinek 13500ebdfe8ba98c05a767660de2ed7c9a19fb49d74Jorim Jaggi @Override 13600ebdfe8ba98c05a767660de2ed7c9a19fb49d74Jorim Jaggi public boolean dispatchTouchEvent(MotionEvent ev) { 13700ebdfe8ba98c05a767660de2ed7c9a19fb49d74Jorim Jaggi if (filterMotionEvent(ev)) { 13800ebdfe8ba98c05a767660de2ed7c9a19fb49d74Jorim Jaggi return super.dispatchTouchEvent(ev); 13900ebdfe8ba98c05a767660de2ed7c9a19fb49d74Jorim Jaggi } 14000ebdfe8ba98c05a767660de2ed7c9a19fb49d74Jorim Jaggi return false; 14100ebdfe8ba98c05a767660de2ed7c9a19fb49d74Jorim Jaggi } 14200ebdfe8ba98c05a767660de2ed7c9a19fb49d74Jorim Jaggi 1431a521f3ea841f6db9686bbec7f950a3883d075aaSelim Cinek protected boolean filterMotionEvent(MotionEvent event) { 14400ebdfe8ba98c05a767660de2ed7c9a19fb49d74Jorim Jaggi return event.getActionMasked() != MotionEvent.ACTION_DOWN 14500ebdfe8ba98c05a767660de2ed7c9a19fb49d74Jorim Jaggi || event.getY() > mClipTopAmount && event.getY() < mActualHeight; 14600ebdfe8ba98c05a767660de2ed7c9a19fb49d74Jorim Jaggi } 14700ebdfe8ba98c05a767660de2ed7c9a19fb49d74Jorim Jaggi 148be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi /** 149be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * Sets the actual height of this notification. This is different than the laid out 150be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * {@link View#getHeight()}, as we want to avoid layouting during scrolling and expanding. 151d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi * 152d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi * @param actualHeight The height of this notification. 153d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi * @param notifyListeners Whether the listener should be informed about the change. 154be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi */ 155d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi public void setActualHeight(int actualHeight, boolean notifyListeners) { 1562580a976ec93a01ed00fae51364ad872bc591d95Jorim Jaggi mActualHeightInitialized = true; 157be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi mActualHeight = actualHeight; 158a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek updateClipping(); 159d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi if (notifyListeners) { 160b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek notifyHeightChanged(false /* needsAnimation */); 161d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 162d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 163d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi 164b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek public void setContentHeight(int contentHeight) { 165b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek setActualHeight(contentHeight + getBottomDecorHeight(), true); 166be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi } 167be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi 168be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi /** 169be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * See {@link #setActualHeight}. 170be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * 1719cbadd3c08a7d7dd3412743dd04aecb16c5a1595Jorim Jaggi * @return The current actual height of this notification. 172be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi */ 173be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi public int getActualHeight() { 174be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi return mActualHeight; 175be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi } 176be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi 177be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi /** 178b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek * This view may have a bottom decor which will be placed below the content. If it has one, this 179b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek * view will be layouted higher than just the content by {@link #mBottomDecorHeight}. 180b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek * @return the height of the decor if it currently has one 181b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek */ 182b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek public int getBottomDecorHeight() { 183b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek return hasBottomDecor() ? mBottomDecorHeight : 0; 184b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek } 185b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek 186b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek /** 187b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek * @return whether this view may have a bottom decor at all. This will force the view to layout 188b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek * itself higher than just it's content 189b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek */ 190b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek protected boolean canHaveBottomDecor() { 191b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek return false; 192b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek } 193b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek 194b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek /** 195b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek * @return whether this view has a decor view below it's content. This will make the intrinsic 196b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek * height from {@link #getIntrinsicHeight()} higher as well 197b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek */ 198b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek protected boolean hasBottomDecor() { 199b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek return false; 200b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek } 201b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek 202b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek /** 203be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * @return The maximum height of this notification. 204be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi */ 205b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek public int getMaxContentHeight() { 2064222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi return getHeight(); 2074222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi } 2084222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi 2094222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi /** 210b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek * @return The minimum content height of this notification. 2114222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi */ 2124222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi public int getMinHeight() { 2134222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi return getHeight(); 2144222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi } 215be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi 216be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi /** 217d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi * Sets the notification as dimmed. The default implementation does nothing. 218d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi * 219d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi * @param dimmed Whether the notification should be dimmed. 220d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi * @param fade Whether an animation should be played to change the state. 221d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi */ 222d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi public void setDimmed(boolean dimmed, boolean fade) { 223d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 224d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi 225d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi /** 226bf370992508c55d1f2493923bdc1834a0710e4baJohn Spurlock * Sets the notification as dark. The default implementation does nothing. 227bf370992508c55d1f2493923bdc1834a0710e4baJohn Spurlock * 228bf370992508c55d1f2493923bdc1834a0710e4baJohn Spurlock * @param dark Whether the notification should be dark. 229bf370992508c55d1f2493923bdc1834a0710e4baJohn Spurlock * @param fade Whether an animation should be played to change the state. 2304e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi * @param delay If fading, the delay of the animation. 231bf370992508c55d1f2493923bdc1834a0710e4baJohn Spurlock */ 2324e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi public void setDark(boolean dark, boolean fade, long delay) { 2334e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi mDark = dark; 2344e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi } 2354e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi 2364e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi public boolean isDark() { 2374e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi return mDark; 238bf370992508c55d1f2493923bdc1834a0710e4baJohn Spurlock } 239bf370992508c55d1f2493923bdc1834a0710e4baJohn Spurlock 240bf370992508c55d1f2493923bdc1834a0710e4baJohn Spurlock /** 241ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi * See {@link #setHideSensitive}. This is a variant which notifies this view in advance about 242ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi * the upcoming state of hiding sensitive notifications. It gets called at the very beginning 243ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi * of a stack scroller update such that the updated intrinsic height (which is dependent on 244ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi * whether private or public layout is showing) gets taken into account into all layout 245ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi * calculations. 246ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi */ 247ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi public void setHideSensitiveForIntrinsicHeight(boolean hideSensitive) { 248ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi } 249ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi 250ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi /** 251ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi * Sets whether the notification should hide its private contents if it is sensitive. 252ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi */ 253ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi public void setHideSensitive(boolean hideSensitive, boolean animated, long delay, 254ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi long duration) { 255ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi } 256ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi 257ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi /** 2589cbadd3c08a7d7dd3412743dd04aecb16c5a1595Jorim Jaggi * @return The desired notification height. 2599cbadd3c08a7d7dd3412743dd04aecb16c5a1595Jorim Jaggi */ 2609cbadd3c08a7d7dd3412743dd04aecb16c5a1595Jorim Jaggi public int getIntrinsicHeight() { 261a5eaa6034dd48fab0f5a232c09ebed35f359963eSelim Cinek return getHeight(); 2629cbadd3c08a7d7dd3412743dd04aecb16c5a1595Jorim Jaggi } 2639cbadd3c08a7d7dd3412743dd04aecb16c5a1595Jorim Jaggi 2649cbadd3c08a7d7dd3412743dd04aecb16c5a1595Jorim Jaggi /** 265be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * Sets the amount this view should be clipped from the top. This is used when an expanded 266be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * notification is scrolling in the top or bottom stack. 267be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * 268be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * @param clipTopAmount The amount of pixels this view should be clipped from top. 269be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi */ 270be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi public void setClipTopAmount(int clipTopAmount) { 271be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi mClipTopAmount = clipTopAmount; 272be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi } 273be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi 274eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public int getClipTopAmount() { 275eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return mClipTopAmount; 276eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 277eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 278be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi public void setOnHeightChangedListener(OnHeightChangedListener listener) { 279be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi mOnHeightChangedListener = listener; 280be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi } 281be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi 282be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi /** 2834222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi * @return Whether we can expand this views content. 284be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi */ 2854222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi public boolean isContentExpandable() { 2864222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi return false; 287be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi } 288be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi 289b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek public void notifyHeightChanged(boolean needsAnimation) { 2909cbadd3c08a7d7dd3412743dd04aecb16c5a1595Jorim Jaggi if (mOnHeightChangedListener != null) { 291b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek mOnHeightChangedListener.onHeightChanged(this, needsAnimation); 2929cbadd3c08a7d7dd3412743dd04aecb16c5a1595Jorim Jaggi } 2939cbadd3c08a7d7dd3412743dd04aecb16c5a1595Jorim Jaggi } 2949cbadd3c08a7d7dd3412743dd04aecb16c5a1595Jorim Jaggi 295c27437b7fd04e682ae2abdf0727a99bf5c6e409dSelim Cinek public boolean isTransparent() { 296c27437b7fd04e682ae2abdf0727a99bf5c6e409dSelim Cinek return false; 297c27437b7fd04e682ae2abdf0727a99bf5c6e409dSelim Cinek } 298c27437b7fd04e682ae2abdf0727a99bf5c6e409dSelim Cinek 299be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi /** 3008efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek * Perform a remove animation on this view. 3018efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek * 30260d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi * @param duration The duration of the remove animation. 3038efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek * @param translationDirection The direction value from [-1 ... 1] indicating in which the 3048efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek * animation should be performed. A value of -1 means that The 3058efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek * remove animation should be performed upwards, 3068efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek * such that the child appears to be going away to the top. 1 3078efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek * Should mean the opposite. 3088efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek * @param onFinishedRunnable A runnable which should be run when the animation is finished. 3098efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek */ 31060d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi public abstract void performRemoveAnimation(long duration, float translationDirection, 3118efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek Runnable onFinishedRunnable); 3128efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 31360d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi public abstract void performAddAnimation(long delay, long duration); 3148efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 3153d2b94bf8e32640e57573ebb17911b1db9440231Selim Cinek public void setBelowSpeedBump(boolean below) { 3163d2b94bf8e32640e57573ebb17911b1db9440231Selim Cinek } 3173d2b94bf8e32640e57573ebb17911b1db9440231Selim Cinek 31831094df5c6e3cb3a4a4faacb091e35eea1f6a5deSelim Cinek public void onHeightReset() { 319e34c6513bb1a4d3e246866c2a7f0619914f18bd3Selim Cinek if (mOnHeightChangedListener != null) { 320e34c6513bb1a4d3e246866c2a7f0619914f18bd3Selim Cinek mOnHeightChangedListener.onReset(this); 321e34c6513bb1a4d3e246866c2a7f0619914f18bd3Selim Cinek } 322a5e211b1f2a8d055b369dadc464dc5d5bc3dd9c1Selim Cinek } 323a5e211b1f2a8d055b369dadc464dc5d5bc3dd9c1Selim Cinek 3248efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek /** 325e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek * This method returns the drawing rect for the view which is different from the regular 326e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek * drawing rect, since we layout all children in the {@link NotificationStackScrollLayout} at 327e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek * position 0 and usually the translation is neglected. Since we are manually clipping this 328e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek * view,we also need to subtract the clipTopAmount from the top. This is needed in order to 329e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek * ensure that accessibility and focusing work correctly. 330e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek * 331e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek * @param outRect The (scrolled) drawing bounds of the view. 332e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek */ 333e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek @Override 334e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek public void getDrawingRect(Rect outRect) { 335e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek super.getDrawingRect(outRect); 336e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek outRect.left += getTranslationX(); 337e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek outRect.right += getTranslationX(); 338e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek outRect.bottom = (int) (outRect.top + getTranslationY() + getActualHeight()); 339e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek outRect.top += getTranslationY() + getClipTopAmount(); 340e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek } 341e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek 342b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek public int getContentHeight() { 343b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek return mActualHeight - getBottomDecorHeight(); 344b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek } 345b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek 346b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek /** 347b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek * @return whether the given child can be ignored for layouting and measuring purposes 348b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek */ 349b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek protected boolean isChildInvisible(View child) { 350b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek return false; 351b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek } 352b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek 353b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek public boolean areChildrenExpanded() { 354b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek return false; 355b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek } 356b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek 357a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek private void updateClipping() { 358a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek mClipRect.set(0, mClipTopOptimization, getWidth(), getActualHeight()); 359a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek setClipBounds(mClipRect); 360a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek } 361a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek 362a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek public int getClipTopOptimization() { 363a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek return mClipTopOptimization; 364a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek } 365a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek 366a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek /** 367a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek * Set that the view will be clipped by a given amount from the top. Contrary to 368a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek * {@link #setClipTopAmount} this amount doesn't effect shadows and the background. 369a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek * 370a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek * @param clipTopOptimization the amount to clip from the top 371a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek */ 372a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek public void setClipTopOptimization(int clipTopOptimization) { 373a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek mClipTopOptimization = clipTopOptimization; 374a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek updateClipping(); 375a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek } 376a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek 377e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek /** 378be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * A listener notifying when {@link #getActualHeight} changes. 379be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi */ 380be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi public interface OnHeightChangedListener { 38130c305ce6283ce1380ad91ef0d221696b32d5a6bJorim Jaggi 38230c305ce6283ce1380ad91ef0d221696b32d5a6bJorim Jaggi /** 38330c305ce6283ce1380ad91ef0d221696b32d5a6bJorim Jaggi * @param view the view for which the height changed, or {@code null} if just the top 38430c305ce6283ce1380ad91ef0d221696b32d5a6bJorim Jaggi * padding or the padding between the elements changed 385b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek * @param needsAnimation whether the view height needs to be animated 38630c305ce6283ce1380ad91ef0d221696b32d5a6bJorim Jaggi */ 387b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek void onHeightChanged(ExpandableView view, boolean needsAnimation); 388a5e211b1f2a8d055b369dadc464dc5d5bc3dd9c1Selim Cinek 389a5e211b1f2a8d055b369dadc464dc5d5bc3dd9c1Selim Cinek /** 390a5e211b1f2a8d055b369dadc464dc5d5bc3dd9c1Selim Cinek * Called when the view is reset and therefore the height will change abruptly 391a5e211b1f2a8d055b369dadc464dc5d5bc3dd9c1Selim Cinek * 392a5e211b1f2a8d055b369dadc464dc5d5bc3dd9c1Selim Cinek * @param view The view which was reset. 393a5e211b1f2a8d055b369dadc464dc5d5bc3dd9c1Selim Cinek */ 394a5e211b1f2a8d055b369dadc464dc5d5bc3dd9c1Selim Cinek void onReset(ExpandableView view); 395be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi } 396be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi} 397