blob: d1a89b4e493f6c1b3fed6eeec29abcca5e5ef75b [file] [log] [blame]
Jorim Jaggibe565df2014-04-28 17:51:23 +02001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
Rohan Shah20790b82018-07-02 17:21:04 -070017package com.android.systemui.statusbar.notification.row;
Jorim Jaggibe565df2014-04-28 17:51:23 +020018
Selim Cinek332c23f2018-03-16 17:37:50 -070019import android.animation.AnimatorListenerAdapter;
Jorim Jaggibe565df2014-04-28 17:51:23 +020020import android.content.Context;
Selim Cinek471e31a2015-12-11 13:39:48 -080021import android.graphics.Paint;
Selim Cineke32010a2014-08-20 23:50:41 +020022import android.graphics.Rect;
Jorim Jaggibe565df2014-04-28 17:51:23 +020023import android.util.AttributeSet;
24import android.view.View;
Selim Cinekc9c00ae2014-05-20 03:33:40 +020025import android.view.ViewGroup;
Selim Cinek24d7cfa2014-05-20 13:50:57 +020026import android.widget.FrameLayout;
Selim Cinekd84a5932015-12-15 11:45:36 -080027
Dave Mankoffa4d195d2018-11-16 13:33:27 -050028import androidx.annotation.Nullable;
29
Selim Cinek30887662018-10-15 17:37:21 -070030import com.android.systemui.Dumpable;
Rohan Shah20790b82018-07-02 17:21:04 -070031import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
32import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
Selim Cinek24d7cfa2014-05-20 13:50:57 +020033
Selim Cinek30887662018-10-15 17:37:21 -070034import java.io.FileDescriptor;
35import java.io.PrintWriter;
Selim Cinek24d7cfa2014-05-20 13:50:57 +020036import java.util.ArrayList;
Dave Mankoffa4d195d2018-11-16 13:33:27 -050037import java.util.List;
Jorim Jaggibe565df2014-04-28 17:51:23 +020038
39/**
40 * An abstract view for expandable views.
41 */
Selim Cinek30887662018-10-15 17:37:21 -070042public abstract class ExpandableView extends FrameLayout implements Dumpable {
Dave Mankoffa4d195d2018-11-16 13:33:27 -050043 private static final String TAG = "ExpandableView";
Selim Cinek24d7cfa2014-05-20 13:50:57 +020044
Selim Cinekeccf4942018-05-30 09:55:36 -070045 public static final float NO_ROUNDNESS = -1;
Selim Cinekb5605e52015-02-20 18:21:41 +010046 protected OnHeightChangedListener mOnHeightChangedListener;
Chris Wren310df312014-10-31 14:29:46 -040047 private int mActualHeight;
Jorim Jaggibe565df2014-04-28 17:51:23 +020048 protected int mClipTopAmount;
Selim Cinekb3dadcc2016-11-21 17:21:13 -080049 protected int mClipBottomAmount;
Selim Cinek53328492018-12-05 14:58:44 -080050 protected int mMinimumHeightForClipping = 0;
51 protected float mExtraWidthForClipping = 0;
Jorim Jaggi4e857f42014-11-17 19:14:04 +010052 private boolean mDark;
Selim Cinek24d7cfa2014-05-20 13:50:57 +020053 private ArrayList<View> mMatchParentViews = new ArrayList<View>();
Selim Cineka272dfe2015-02-20 18:12:28 +010054 private static Rect mClipRect = new Rect();
Selim Cinek2cd45df2015-06-09 18:00:07 -070055 private boolean mWillBeGone;
Selim Cinek9c17b772015-07-07 20:37:09 -070056 private int mMinClipTopAmount = 0;
Selim Cinek4ffd6362015-12-29 15:12:23 +010057 private boolean mClipToActualHeight = true;
Adrian Roos14503e22016-03-09 14:01:24 -080058 private boolean mChangingPosition = false;
Selim Cinekd1395642016-04-28 12:22:42 -070059 private ViewGroup mTransientContainer;
Selim Cinekeccb5de2016-10-28 15:04:05 -070060 private boolean mInShelf;
61 private boolean mTransformingInShelf;
Dave Mankoffc56db332018-12-10 11:31:49 -050062 private final ExpandableViewState mViewState;
Jorim Jaggibe565df2014-04-28 17:51:23 +020063
64 public ExpandableView(Context context, AttributeSet attrs) {
65 super(context, attrs);
Dave Mankoffc56db332018-12-10 11:31:49 -050066 mViewState = createExpandableViewState();
Selim Cinek24d7cfa2014-05-20 13:50:57 +020067 }
68
69 @Override
70 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Selim Cinek471e31a2015-12-11 13:39:48 -080071 final int givenSize = MeasureSpec.getSize(heightMeasureSpec);
felkachang79ca96d2018-04-27 16:55:40 +080072 final int viewHorizontalPadding = getPaddingStart() + getPaddingEnd();
Selim Cinekd84a5932015-12-15 11:45:36 -080073 int ownMaxHeight = Integer.MAX_VALUE;
Selim Cinek6f145cf2015-05-18 15:16:08 -070074 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
Selim Cineka69f2a62015-12-11 17:28:12 -080075 if (heightMode != MeasureSpec.UNSPECIFIED && givenSize != 0) {
Selim Cinek471e31a2015-12-11 13:39:48 -080076 ownMaxHeight = Math.min(givenSize, ownMaxHeight);
Selim Cinek6f145cf2015-05-18 15:16:08 -070077 }
Selim Cinek24d7cfa2014-05-20 13:50:57 +020078 int newHeightSpec = MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.AT_MOST);
79 int maxChildHeight = 0;
80 int childCount = getChildCount();
81 for (int i = 0; i < childCount; i++) {
82 View child = getChildAt(i);
Selim Cinek263398f2015-10-21 17:40:23 -070083 if (child.getVisibility() == GONE) {
Selim Cinekb5605e52015-02-20 18:21:41 +010084 continue;
85 }
Selim Cinek24d7cfa2014-05-20 13:50:57 +020086 int childHeightSpec = newHeightSpec;
87 ViewGroup.LayoutParams layoutParams = child.getLayoutParams();
88 if (layoutParams.height != ViewGroup.LayoutParams.MATCH_PARENT) {
89 if (layoutParams.height >= 0) {
90 // An actual height is set
Selim Cineka69f2a62015-12-11 17:28:12 -080091 childHeightSpec = layoutParams.height > ownMaxHeight
Selim Cinek24d7cfa2014-05-20 13:50:57 +020092 ? MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.EXACTLY)
93 : MeasureSpec.makeMeasureSpec(layoutParams.height, MeasureSpec.EXACTLY);
94 }
felkachang79ca96d2018-04-27 16:55:40 +080095 child.measure(getChildMeasureSpec(
96 widthMeasureSpec, viewHorizontalPadding, layoutParams.width),
Jorim Jaggib741f052014-06-03 19:57:26 +020097 childHeightSpec);
Selim Cinek24d7cfa2014-05-20 13:50:57 +020098 int childHeight = child.getMeasuredHeight();
99 maxChildHeight = Math.max(maxChildHeight, childHeight);
100 } else {
101 mMatchParentViews.add(child);
102 }
103 }
Selim Cineka69f2a62015-12-11 17:28:12 -0800104 int ownHeight = heightMode == MeasureSpec.EXACTLY
105 ? givenSize : Math.min(ownMaxHeight, maxChildHeight);
Selim Cinek24d7cfa2014-05-20 13:50:57 +0200106 newHeightSpec = MeasureSpec.makeMeasureSpec(ownHeight, MeasureSpec.EXACTLY);
107 for (View child : mMatchParentViews) {
Jorim Jaggib741f052014-06-03 19:57:26 +0200108 child.measure(getChildMeasureSpec(
felkachang79ca96d2018-04-27 16:55:40 +0800109 widthMeasureSpec, viewHorizontalPadding, child.getLayoutParams().width),
Jorim Jaggib741f052014-06-03 19:57:26 +0200110 newHeightSpec);
Selim Cinek24d7cfa2014-05-20 13:50:57 +0200111 }
112 mMatchParentViews.clear();
113 int width = MeasureSpec.getSize(widthMeasureSpec);
114 setMeasuredDimension(width, ownHeight);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200115 }
116
117 @Override
Jorim Jaggibe565df2014-04-28 17:51:23 +0200118 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Selim Cinek24d7cfa2014-05-20 13:50:57 +0200119 super.onLayout(changed, left, top, right, bottom);
Selim Cinekba67acf2015-04-10 17:00:42 -0700120 updateClipping();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200121 }
122
Jorim Jaggi00ebdfe2014-05-02 17:29:56 +0200123 @Override
Selim Cineka69f2a62015-12-11 17:28:12 -0800124 public boolean pointInView(float localX, float localY, float slop) {
125 float top = mClipTopAmount;
126 float bottom = mActualHeight;
127 return localX >= -slop && localY >= top - slop && localX < ((mRight - mLeft) + slop) &&
128 localY < (bottom + slop);
Jorim Jaggi00ebdfe2014-05-02 17:29:56 +0200129 }
130
Jorim Jaggibe565df2014-04-28 17:51:23 +0200131 /**
132 * Sets the actual height of this notification. This is different than the laid out
133 * {@link View#getHeight()}, as we want to avoid layouting during scrolling and expanding.
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200134 *
135 * @param actualHeight The height of this notification.
136 * @param notifyListeners Whether the listener should be informed about the change.
Jorim Jaggibe565df2014-04-28 17:51:23 +0200137 */
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200138 public void setActualHeight(int actualHeight, boolean notifyListeners) {
Jorim Jaggibe565df2014-04-28 17:51:23 +0200139 mActualHeight = actualHeight;
Selim Cineka272dfe2015-02-20 18:12:28 +0100140 updateClipping();
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200141 if (notifyListeners) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100142 notifyHeightChanged(false /* needsAnimation */);
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200143 }
144 }
145
Selim Cinekb0ee18f2017-12-21 16:15:53 -0800146 /**
147 * Set the distance to the top roundness, from where we should start clipping a value above
148 * or equal to 0 is the effective distance, and if a value below 0 is received, there should
149 * be no clipping.
150 */
151 public void setDistanceToTopRoundness(float distanceToTopRoundness) {
152 }
153
Selim Cinekeef84282015-10-30 16:28:00 -0700154 public void setActualHeight(int actualHeight) {
155 setActualHeight(actualHeight, true /* notifyListeners */);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200156 }
157
158 /**
159 * See {@link #setActualHeight}.
160 *
Jorim Jaggi9cbadd32014-05-01 20:18:31 +0200161 * @return The current actual height of this notification.
Jorim Jaggibe565df2014-04-28 17:51:23 +0200162 */
163 public int getActualHeight() {
164 return mActualHeight;
165 }
166
Selim Cinek2627d722018-01-19 12:16:49 -0800167 public boolean isExpandAnimationRunning() {
168 return false;
169 }
170
Jorim Jaggibe565df2014-04-28 17:51:23 +0200171 /**
172 * @return The maximum height of this notification.
173 */
Selim Cinekb5605e52015-02-20 18:21:41 +0100174 public int getMaxContentHeight() {
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200175 return getHeight();
176 }
177
178 /**
Selim Cinekeb3fc3d2017-09-15 13:37:14 -0700179 * @return The minimum content height of this notification. This also respects the temporary
180 * states of the view.
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200181 */
182 public int getMinHeight() {
Selim Cinekeb3fc3d2017-09-15 13:37:14 -0700183 return getMinHeight(false /* ignoreTemporaryStates */);
184 }
185
186 /**
187 * Get the minimum height of this view.
188 *
189 * @param ignoreTemporaryStates should temporary states be ignored like the guts or heads-up.
190 *
191 * @return The minimum height that this view needs.
192 */
193 public int getMinHeight(boolean ignoreTemporaryStates) {
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200194 return getHeight();
195 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200196
197 /**
Selim Cinek567e8452016-03-24 10:54:56 -0700198 * @return The collapsed height of this view. Note that this might be different
199 * than {@link #getMinHeight()} because some elements like groups may have different sizes when
200 * they are system expanded.
Selim Cinek816c8e42015-11-19 12:00:45 -0800201 */
Selim Cinek567e8452016-03-24 10:54:56 -0700202 public int getCollapsedHeight() {
Selim Cinek816c8e42015-11-19 12:00:45 -0800203 return getHeight();
204 }
205
206 /**
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200207 * Sets the notification as dimmed. The default implementation does nothing.
208 *
209 * @param dimmed Whether the notification should be dimmed.
210 * @param fade Whether an animation should be played to change the state.
211 */
212 public void setDimmed(boolean dimmed, boolean fade) {
213 }
214
215 /**
John Spurlockbf370992014-06-17 13:58:31 -0400216 * Sets the notification as dark. The default implementation does nothing.
217 *
218 * @param dark Whether the notification should be dark.
219 * @param fade Whether an animation should be played to change the state.
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100220 * @param delay If fading, the delay of the animation.
John Spurlockbf370992014-06-17 13:58:31 -0400221 */
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100222 public void setDark(boolean dark, boolean fade, long delay) {
223 mDark = dark;
224 }
225
226 public boolean isDark() {
227 return mDark;
John Spurlockbf370992014-06-17 13:58:31 -0400228 }
229
Selim Cinekd9b7dd42017-11-10 17:53:47 -0800230 public boolean isRemoved() {
231 return false;
232 }
233
John Spurlockbf370992014-06-17 13:58:31 -0400234 /**
Jorim Jaggiae441282014-08-01 02:45:18 +0200235 * See {@link #setHideSensitive}. This is a variant which notifies this view in advance about
236 * the upcoming state of hiding sensitive notifications. It gets called at the very beginning
237 * of a stack scroller update such that the updated intrinsic height (which is dependent on
238 * whether private or public layout is showing) gets taken into account into all layout
239 * calculations.
240 */
241 public void setHideSensitiveForIntrinsicHeight(boolean hideSensitive) {
242 }
243
244 /**
245 * Sets whether the notification should hide its private contents if it is sensitive.
246 */
247 public void setHideSensitive(boolean hideSensitive, boolean animated, long delay,
248 long duration) {
249 }
250
251 /**
Jorim Jaggi9cbadd32014-05-01 20:18:31 +0200252 * @return The desired notification height.
253 */
254 public int getIntrinsicHeight() {
Selim Cineka5eaa602014-05-12 21:27:47 +0200255 return getHeight();
Jorim Jaggi9cbadd32014-05-01 20:18:31 +0200256 }
257
258 /**
Jorim Jaggibe565df2014-04-28 17:51:23 +0200259 * Sets the amount this view should be clipped from the top. This is used when an expanded
260 * notification is scrolling in the top or bottom stack.
261 *
262 * @param clipTopAmount The amount of pixels this view should be clipped from top.
263 */
264 public void setClipTopAmount(int clipTopAmount) {
265 mClipTopAmount = clipTopAmount;
Mady Mellorc128f222016-04-26 11:42:46 -0700266 updateClipping();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200267 }
268
Selim Cineka686b2c2016-10-26 13:58:27 -0700269 /**
270 * Set the amount the the notification is clipped on the bottom in addition to the regular
271 * clipping. This is mainly used to clip something in a non-animated way without changing the
272 * actual height of the notification and is purely visual.
273 *
274 * @param clipBottomAmount the amount to clip.
275 */
276 public void setClipBottomAmount(int clipBottomAmount) {
277 mClipBottomAmount = clipBottomAmount;
278 updateClipping();
279 }
280
Selim Cinekeb973562014-05-02 17:07:49 +0200281 public int getClipTopAmount() {
282 return mClipTopAmount;
283 }
284
Selim Cinekb3dadcc2016-11-21 17:21:13 -0800285 public int getClipBottomAmount() {
Selim Cineka686b2c2016-10-26 13:58:27 -0700286 return mClipBottomAmount;
287 }
288
Jorim Jaggibe565df2014-04-28 17:51:23 +0200289 public void setOnHeightChangedListener(OnHeightChangedListener listener) {
290 mOnHeightChangedListener = listener;
291 }
292
293 /**
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200294 * @return Whether we can expand this views content.
Jorim Jaggibe565df2014-04-28 17:51:23 +0200295 */
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200296 public boolean isContentExpandable() {
297 return false;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200298 }
299
Selim Cinekb5605e52015-02-20 18:21:41 +0100300 public void notifyHeightChanged(boolean needsAnimation) {
Jorim Jaggi9cbadd32014-05-01 20:18:31 +0200301 if (mOnHeightChangedListener != null) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100302 mOnHeightChangedListener.onHeightChanged(this, needsAnimation);
Jorim Jaggi9cbadd32014-05-01 20:18:31 +0200303 }
304 }
305
Selim Cinekc27437b2014-05-14 10:23:33 +0200306 public boolean isTransparent() {
307 return false;
308 }
309
Jorim Jaggibe565df2014-04-28 17:51:23 +0200310 /**
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200311 * Perform a remove animation on this view.
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200312 * @param duration The duration of the remove animation.
Selim Cinek332c23f2018-03-16 17:37:50 -0700313 * @param delay The delay of the animation
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200314 * @param translationDirection The direction value from [-1 ... 1] indicating in which the
Gus Prevas211181532018-12-13 14:49:33 -0500315 * animation should be performed. A value of -1 means that The
316 * remove animation should be performed upwards,
317 * such that the child appears to be going away to the top. 1
318 * Should mean the opposite.
Selim Cinek332c23f2018-03-16 17:37:50 -0700319 * @param isHeadsUpAnimation Is this a headsUp animation.
320 * @param endLocation The location where the horizonal heads up disappear animation should end.
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200321 * @param onFinishedRunnable A runnable which should be run when the animation is finished.
Selim Cinek332c23f2018-03-16 17:37:50 -0700322 * @param animationListener An animation listener to add to the animation.
Gus Prevas211181532018-12-13 14:49:33 -0500323 *
324 * @return The additional delay, in milliseconds, that this view needs to add before the
325 * animation starts.
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200326 */
Gus Prevas211181532018-12-13 14:49:33 -0500327 public abstract long performRemoveAnimation(long duration,
Selim Cinek332c23f2018-03-16 17:37:50 -0700328 long delay, float translationDirection, boolean isHeadsUpAnimation, float endLocation,
329 Runnable onFinishedRunnable,
330 AnimatorListenerAdapter animationListener);
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200331
Selim Cinek332c23f2018-03-16 17:37:50 -0700332 public abstract void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear);
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200333
Selim Cinek281c2022016-10-13 19:14:43 -0700334 /**
Selim Cinekdb167372016-11-17 15:41:17 -0800335 * Set the notification appearance to be below the speed bump.
Selim Cinek281c2022016-10-13 19:14:43 -0700336 * @param below true if it is below.
337 */
Selim Cinekdb167372016-11-17 15:41:17 -0800338 public void setBelowSpeedBump(boolean below) {
Selim Cinek3d2b94bf2014-07-02 22:12:47 +0200339 }
340
Selim Cinekd127d792016-11-01 19:11:41 -0700341 public int getPinnedHeadsUpHeight() {
342 return getIntrinsicHeight();
343 }
344
345
Mady Mellor34958fa2016-02-23 09:52:17 -0800346 /**
347 * Sets the translation of the view.
348 */
349 public void setTranslation(float translation) {
350 setTranslationX(translation);
351 }
352
353 /**
354 * Gets the translation of the view.
355 */
356 public float getTranslation() {
357 return getTranslationX();
358 }
359
Selim Cinek31094df2014-08-14 19:28:15 +0200360 public void onHeightReset() {
Selim Cineke34c6512014-08-14 11:19:41 +0200361 if (mOnHeightChangedListener != null) {
362 mOnHeightChangedListener.onReset(this);
363 }
Selim Cineka5e211b2014-08-11 17:35:48 +0200364 }
365
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200366 /**
Selim Cineke32010a2014-08-20 23:50:41 +0200367 * This method returns the drawing rect for the view which is different from the regular
368 * drawing rect, since we layout all children in the {@link NotificationStackScrollLayout} at
369 * position 0 and usually the translation is neglected. Since we are manually clipping this
370 * view,we also need to subtract the clipTopAmount from the top. This is needed in order to
371 * ensure that accessibility and focusing work correctly.
372 *
373 * @param outRect The (scrolled) drawing bounds of the view.
374 */
375 @Override
376 public void getDrawingRect(Rect outRect) {
377 super.getDrawingRect(outRect);
378 outRect.left += getTranslationX();
379 outRect.right += getTranslationX();
380 outRect.bottom = (int) (outRect.top + getTranslationY() + getActualHeight());
381 outRect.top += getTranslationY() + getClipTopAmount();
382 }
383
Adrian Rooscd55f432015-06-10 16:42:53 -0700384 @Override
385 public void getBoundsOnScreen(Rect outRect, boolean clipToParent) {
386 super.getBoundsOnScreen(outRect, clipToParent);
Adrian Roos98dd7f12016-06-14 15:51:40 -0700387 if (getTop() + getTranslationY() < 0) {
388 // We got clipped to the parent here - make sure we undo that.
389 outRect.top += getTop() + getTranslationY();
390 }
Adrian Roosb44f5482015-06-11 12:19:16 -0700391 outRect.bottom = outRect.top + getActualHeight();
Mady Mellorc128f222016-04-26 11:42:46 -0700392 outRect.top += getClipTopAmount();
Adrian Rooscd55f432015-06-10 16:42:53 -0700393 }
394
Selim Cinek263398f2015-10-21 17:40:23 -0700395 public boolean isSummaryWithChildren() {
Selim Cinekb5605e52015-02-20 18:21:41 +0100396 return false;
397 }
398
399 public boolean areChildrenExpanded() {
400 return false;
401 }
402
Selim Cinek2627d722018-01-19 12:16:49 -0800403 protected void updateClipping() {
404 if (mClipToActualHeight && shouldClipToActualHeight()) {
Mady Mellorc128f222016-04-26 11:42:46 -0700405 int top = getClipTopAmount();
Selim Cinek53328492018-12-05 14:58:44 -0800406 int bottom = Math.max(Math.max(getActualHeight() + getExtraBottomPadding()
407 - mClipBottomAmount, top), mMinimumHeightForClipping);
408 int halfExtraWidth = (int) (mExtraWidthForClipping / 2.0f);
409 mClipRect.set(-halfExtraWidth, top, getWidth() + halfExtraWidth, bottom);
Selim Cinek4ffd6362015-12-29 15:12:23 +0100410 setClipBounds(mClipRect);
411 } else {
412 setClipBounds(null);
Selim Cinekf92a1fd2015-07-31 16:10:32 -0700413 }
Selim Cinek4ffd6362015-12-29 15:12:23 +0100414 }
415
Selim Cinek53328492018-12-05 14:58:44 -0800416 public void setMinimumHeightForClipping(int minimumHeightForClipping) {
417 mMinimumHeightForClipping = minimumHeightForClipping;
418 updateClipping();
419 }
420
421 public void setExtraWidthForClipping(float extraWidthForClipping) {
422 mExtraWidthForClipping = extraWidthForClipping;
423 updateClipping();
424 }
425
Selim Cinek99e9adf2018-03-15 09:17:47 -0700426 public float getHeaderVisibleAmount() {
427 return 1.0f;
428 }
429
Selim Cinek2627d722018-01-19 12:16:49 -0800430 protected boolean shouldClipToActualHeight() {
431 return true;
432 }
433
Selim Cinek4ffd6362015-12-29 15:12:23 +0100434 public void setClipToActualHeight(boolean clipToActualHeight) {
435 mClipToActualHeight = clipToActualHeight;
436 updateClipping();
Selim Cineka272dfe2015-02-20 18:12:28 +0100437 }
438
Selim Cinek2cd45df2015-06-09 18:00:07 -0700439 public boolean willBeGone() {
440 return mWillBeGone;
441 }
442
443 public void setWillBeGone(boolean willBeGone) {
444 mWillBeGone = willBeGone;
445 }
446
Selim Cinek9c17b772015-07-07 20:37:09 -0700447 public int getMinClipTopAmount() {
448 return mMinClipTopAmount;
449 }
450
451 public void setMinClipTopAmount(int minClipTopAmount) {
452 mMinClipTopAmount = minClipTopAmount;
453 }
454
Selim Cinek471e31a2015-12-11 13:39:48 -0800455 @Override
456 public void setLayerType(int layerType, Paint paint) {
457 if (hasOverlappingRendering()) {
458 super.setLayerType(layerType, paint);
459 }
460 }
461
462 @Override
463 public boolean hasOverlappingRendering() {
Selim Cineka69f2a62015-12-11 17:28:12 -0800464 // Otherwise it will be clipped
465 return super.hasOverlappingRendering() && getActualHeight() <= getHeight();
Selim Cinek471e31a2015-12-11 13:39:48 -0800466 }
467
Selim Cinek42357e02016-02-24 18:48:01 -0800468 /**
Selim Cineka7ed2c12017-01-23 20:47:24 -0800469 * @return an amount between -1 and 1 of increased padding that this child needs. 1 means it
470 * needs a full increased padding while -1 means it needs no padding at all. For 0.0f the normal
471 * padding is applied.
Selim Cinek42357e02016-02-24 18:48:01 -0800472 */
473 public float getIncreasedPaddingAmount() {
474 return 0.0f;
Selim Cinek61633a82016-01-25 15:54:10 -0800475 }
476
Selim Cinek3776fe02016-02-04 13:32:43 -0800477 public boolean mustStayOnScreen() {
478 return false;
479 }
480
Selim Cinek33223572016-02-19 19:32:22 -0800481 public void setFakeShadowIntensity(float shadowIntensity, float outlineAlpha, int shadowYEnd,
482 int outlineTranslation) {
483 }
484
485 public float getOutlineAlpha() {
486 return 0.0f;
487 }
488
489 public int getOutlineTranslation() {
490 return 0;
491 }
492
Adrian Roos14503e22016-03-09 14:01:24 -0800493 public void setChangingPosition(boolean changingPosition) {
494 mChangingPosition = changingPosition;
495 }
496
497 public boolean isChangingPosition() {
498 return mChangingPosition;
499 }
500
Selim Cinekd1395642016-04-28 12:22:42 -0700501 public void setTransientContainer(ViewGroup transientContainer) {
502 mTransientContainer = transientContainer;
503 }
504
505 public ViewGroup getTransientContainer() {
506 return mTransientContainer;
507 }
508
Selim Cineke32010a2014-08-20 23:50:41 +0200509 /**
Mady Mellorb0a82462016-04-30 17:31:02 -0700510 * @return padding used to alter how much of the view is clipped.
511 */
512 public int getExtraBottomPadding() {
513 return 0;
514 }
515
516 /**
517 * @return true if the group's expansion state is changing, false otherwise.
518 */
519 public boolean isGroupExpansionChanging() {
520 return false;
521 }
522
523 public boolean isGroupExpanded() {
524 return false;
525 }
526
Selim Cinek9b9d6e12017-11-30 12:29:47 +0100527 public void setHeadsUpIsVisible() {
528 }
529
Mady Mellorb0a82462016-04-30 17:31:02 -0700530 public boolean isChildInGroup() {
531 return false;
532 }
533
Adrian Roos599be342016-06-13 14:54:39 -0700534 public void setActualHeightAnimating(boolean animating) {}
535
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500536 protected ExpandableViewState createExpandableViewState() {
Selim Cinekbbcebde2016-11-09 18:28:20 -0800537 return new ExpandableViewState();
538 }
539
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500540 /** Sets {@link ExpandableViewState} to default state. */
541 public ExpandableViewState resetViewState() {
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500542 // initialize with the default values of the view
543 mViewState.height = getIntrinsicHeight();
544 mViewState.gone = getVisibility() == View.GONE;
545 mViewState.alpha = 1f;
546 mViewState.notGoneIndex = -1;
547 mViewState.xTranslation = getTranslationX();
548 mViewState.hidden = false;
549 mViewState.scaleX = getScaleX();
550 mViewState.scaleY = getScaleY();
551 mViewState.inShelf = false;
552 mViewState.headsUpIsVisible = false;
553
554 // handling reset for child notifications
555 if (this instanceof ExpandableNotificationRow) {
556 ExpandableNotificationRow row = (ExpandableNotificationRow) this;
557 List<ExpandableNotificationRow> children = row.getNotificationChildren();
558 if (row.isSummaryWithChildren() && children != null) {
559 for (ExpandableNotificationRow childRow : children) {
560 childRow.resetViewState();
561 }
562 }
563 }
564
565 return mViewState;
566 }
567
568 @Nullable public ExpandableViewState getViewState() {
569 return mViewState;
570 }
571
572 /** Applies internal {@link ExpandableViewState} to this view. */
573 public void applyViewState() {
Dave Mankoffc56db332018-12-10 11:31:49 -0500574 if (!mViewState.gone) {
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500575 mViewState.applyToView(this);
576 }
577 }
578
Mady Mellorb0a82462016-04-30 17:31:02 -0700579 /**
Selim Cinek281c2022016-10-13 19:14:43 -0700580 * @return whether the current view doesn't add height to the overall content. This means that
581 * if it is added to a list of items, it's content will still have the same height.
582 * An example is the notification shelf, that is always placed on top of another view.
583 */
584 public boolean hasNoContentHeight() {
585 return false;
586 }
587
588 /**
Selim Cinekeccb5de2016-10-28 15:04:05 -0700589 * @param inShelf whether the view is currently fully in the notification shelf.
590 */
591 public void setInShelf(boolean inShelf) {
592 mInShelf = inShelf;
593 }
594
595 public boolean isInShelf() {
596 return mInShelf;
597 }
598
599 /**
600 * @param transformingInShelf whether the view is currently transforming into the shelf in an
601 * animated way
602 */
603 public void setTransformingInShelf(boolean transformingInShelf) {
604 mTransformingInShelf = transformingInShelf;
605 }
606
607 public boolean isTransformingIntoShelf() {
608 return mTransformingInShelf;
609 }
610
Selim Cinekd127d792016-11-01 19:11:41 -0700611 public boolean isAboveShelf() {
612 return false;
613 }
614
Selim Cinekc25989e2018-02-16 16:42:14 -0800615 public boolean hasExpandingChild() {
616 return false;
617 }
618
Selim Cinek30887662018-10-15 17:37:21 -0700619 @Override
620 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
621 }
622
Selim Cinekeccb5de2016-10-28 15:04:05 -0700623 /**
Jorim Jaggibe565df2014-04-28 17:51:23 +0200624 * A listener notifying when {@link #getActualHeight} changes.
625 */
626 public interface OnHeightChangedListener {
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200627
628 /**
629 * @param view the view for which the height changed, or {@code null} if just the top
630 * padding or the padding between the elements changed
Selim Cinekb5605e52015-02-20 18:21:41 +0100631 * @param needsAnimation whether the view height needs to be animated
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200632 */
Selim Cinekb5605e52015-02-20 18:21:41 +0100633 void onHeightChanged(ExpandableView view, boolean needsAnimation);
Selim Cineka5e211b2014-08-11 17:35:48 +0200634
635 /**
636 * Called when the view is reset and therefore the height will change abruptly
637 *
638 * @param view The view which was reset.
639 */
640 void onReset(ExpandableView view);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200641 }
642}