blob: 127ff6cd4f931eb1390677a1b6dc2b77e921ed56 [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
17package com.android.systemui.statusbar;
18
19import android.content.Context;
Jorim Jaggibe565df2014-04-28 17:51:23 +020020import android.util.AttributeSet;
Jorim Jaggi00ebdfe2014-05-02 17:29:56 +020021import android.view.MotionEvent;
Jorim Jaggibe565df2014-04-28 17:51:23 +020022import android.view.View;
Selim Cinekc9c00ae2014-05-20 03:33:40 +020023import android.view.ViewGroup;
Selim Cinek24d7cfa2014-05-20 13:50:57 +020024import android.widget.FrameLayout;
25import com.android.systemui.R;
26
27import java.util.ArrayList;
Jorim Jaggibe565df2014-04-28 17:51:23 +020028
29/**
30 * An abstract view for expandable views.
31 */
Selim Cinek24d7cfa2014-05-20 13:50:57 +020032public abstract class ExpandableView extends FrameLayout {
33
34 private final int mMaxNotificationHeight;
Jorim Jaggibe565df2014-04-28 17:51:23 +020035
36 private OnHeightChangedListener mOnHeightChangedListener;
37 protected int mActualHeight;
38 protected int mClipTopAmount;
Jorim Jaggibe565df2014-04-28 17:51:23 +020039 private boolean mActualHeightInitialized;
Selim Cinek24d7cfa2014-05-20 13:50:57 +020040 private ArrayList<View> mMatchParentViews = new ArrayList<View>();
Jorim Jaggibe565df2014-04-28 17:51:23 +020041
42 public ExpandableView(Context context, AttributeSet attrs) {
43 super(context, attrs);
Selim Cinek24d7cfa2014-05-20 13:50:57 +020044 mMaxNotificationHeight = getResources().getDimensionPixelSize(
45 R.dimen.notification_max_height);
46 }
47
48 @Override
49 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
50 int ownMaxHeight = mMaxNotificationHeight;
51 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
52 boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
53 boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST;
54 if (hasFixedHeight || isHeightLimited) {
55 int size = MeasureSpec.getSize(heightMeasureSpec);
56 ownMaxHeight = Math.min(ownMaxHeight, size);
57 }
58 int newHeightSpec = MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.AT_MOST);
59 int maxChildHeight = 0;
60 int childCount = getChildCount();
61 for (int i = 0; i < childCount; i++) {
62 View child = getChildAt(i);
63 int childHeightSpec = newHeightSpec;
64 ViewGroup.LayoutParams layoutParams = child.getLayoutParams();
65 if (layoutParams.height != ViewGroup.LayoutParams.MATCH_PARENT) {
66 if (layoutParams.height >= 0) {
67 // An actual height is set
68 childHeightSpec = layoutParams.height > ownMaxHeight
69 ? MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.EXACTLY)
70 : MeasureSpec.makeMeasureSpec(layoutParams.height, MeasureSpec.EXACTLY);
71 }
Jorim Jaggib741f052014-06-03 19:57:26 +020072 child.measure(
73 getChildMeasureSpec(widthMeasureSpec, 0 /* padding */, layoutParams.width),
74 childHeightSpec);
Selim Cinek24d7cfa2014-05-20 13:50:57 +020075 int childHeight = child.getMeasuredHeight();
76 maxChildHeight = Math.max(maxChildHeight, childHeight);
77 } else {
78 mMatchParentViews.add(child);
79 }
80 }
81 int ownHeight = hasFixedHeight ? ownMaxHeight : maxChildHeight;
82 newHeightSpec = MeasureSpec.makeMeasureSpec(ownHeight, MeasureSpec.EXACTLY);
83 for (View child : mMatchParentViews) {
Jorim Jaggib741f052014-06-03 19:57:26 +020084 child.measure(getChildMeasureSpec(
85 widthMeasureSpec, 0 /* padding */, child.getLayoutParams().width),
86 newHeightSpec);
Selim Cinek24d7cfa2014-05-20 13:50:57 +020087 }
88 mMatchParentViews.clear();
89 int width = MeasureSpec.getSize(widthMeasureSpec);
90 setMeasuredDimension(width, ownHeight);
Jorim Jaggibe565df2014-04-28 17:51:23 +020091 }
92
93 @Override
Jorim Jaggibe565df2014-04-28 17:51:23 +020094 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Selim Cinek24d7cfa2014-05-20 13:50:57 +020095 super.onLayout(changed, left, top, right, bottom);
Jorim Jaggibe565df2014-04-28 17:51:23 +020096 if (!mActualHeightInitialized && mActualHeight == 0) {
Jorim Jaggi3c3c3fc2014-05-20 23:16:42 +020097 setActualHeight(getInitialHeight());
Jorim Jaggibe565df2014-04-28 17:51:23 +020098 }
Jorim Jaggibe565df2014-04-28 17:51:23 +020099 }
100
Selim Cinekc27437b2014-05-14 10:23:33 +0200101 protected int getInitialHeight() {
102 return getHeight();
103 }
104
Jorim Jaggi00ebdfe2014-05-02 17:29:56 +0200105 @Override
106 public boolean dispatchTouchEvent(MotionEvent ev) {
107 if (filterMotionEvent(ev)) {
108 return super.dispatchTouchEvent(ev);
109 }
110 return false;
111 }
112
113 private boolean filterMotionEvent(MotionEvent event) {
114 return event.getActionMasked() != MotionEvent.ACTION_DOWN
115 || event.getY() > mClipTopAmount && event.getY() < mActualHeight;
116 }
117
Jorim Jaggibe565df2014-04-28 17:51:23 +0200118 /**
119 * Sets the actual height of this notification. This is different than the laid out
120 * {@link View#getHeight()}, as we want to avoid layouting during scrolling and expanding.
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200121 *
122 * @param actualHeight The height of this notification.
123 * @param notifyListeners Whether the listener should be informed about the change.
Jorim Jaggibe565df2014-04-28 17:51:23 +0200124 */
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200125 public void setActualHeight(int actualHeight, boolean notifyListeners) {
Jorim Jaggi2580a9762014-06-25 03:08:25 +0200126 mActualHeightInitialized = true;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200127 mActualHeight = actualHeight;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200128 if (notifyListeners) {
129 notifyHeightChanged();
130 }
131 }
132
133 public void setActualHeight(int actualHeight) {
134 setActualHeight(actualHeight, true);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200135 }
136
137 /**
138 * See {@link #setActualHeight}.
139 *
Jorim Jaggi9cbadd32014-05-01 20:18:31 +0200140 * @return The current actual height of this notification.
Jorim Jaggibe565df2014-04-28 17:51:23 +0200141 */
142 public int getActualHeight() {
143 return mActualHeight;
144 }
145
146 /**
147 * @return The maximum height of this notification.
148 */
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200149 public int getMaxHeight() {
150 return getHeight();
151 }
152
153 /**
154 * @return The minimum height of this notification.
155 */
156 public int getMinHeight() {
157 return getHeight();
158 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200159
160 /**
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200161 * Sets the notification as dimmed. The default implementation does nothing.
162 *
163 * @param dimmed Whether the notification should be dimmed.
164 * @param fade Whether an animation should be played to change the state.
165 */
166 public void setDimmed(boolean dimmed, boolean fade) {
167 }
168
169 /**
John Spurlockbf370992014-06-17 13:58:31 -0400170 * Sets the notification as dark. The default implementation does nothing.
171 *
172 * @param dark Whether the notification should be dark.
173 * @param fade Whether an animation should be played to change the state.
174 */
175 public void setDark(boolean dark, boolean fade) {
176 }
177
178 /**
Jorim Jaggiae441282014-08-01 02:45:18 +0200179 * See {@link #setHideSensitive}. This is a variant which notifies this view in advance about
180 * the upcoming state of hiding sensitive notifications. It gets called at the very beginning
181 * of a stack scroller update such that the updated intrinsic height (which is dependent on
182 * whether private or public layout is showing) gets taken into account into all layout
183 * calculations.
184 */
185 public void setHideSensitiveForIntrinsicHeight(boolean hideSensitive) {
186 }
187
188 /**
189 * Sets whether the notification should hide its private contents if it is sensitive.
190 */
191 public void setHideSensitive(boolean hideSensitive, boolean animated, long delay,
192 long duration) {
193 }
194
195 /**
Jorim Jaggi9cbadd32014-05-01 20:18:31 +0200196 * @return The desired notification height.
197 */
198 public int getIntrinsicHeight() {
Selim Cineka5eaa602014-05-12 21:27:47 +0200199 return getHeight();
Jorim Jaggi9cbadd32014-05-01 20:18:31 +0200200 }
201
202 /**
Jorim Jaggibe565df2014-04-28 17:51:23 +0200203 * Sets the amount this view should be clipped from the top. This is used when an expanded
204 * notification is scrolling in the top or bottom stack.
205 *
206 * @param clipTopAmount The amount of pixels this view should be clipped from top.
207 */
208 public void setClipTopAmount(int clipTopAmount) {
209 mClipTopAmount = clipTopAmount;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200210 }
211
Selim Cinekeb973562014-05-02 17:07:49 +0200212 public int getClipTopAmount() {
213 return mClipTopAmount;
214 }
215
Jorim Jaggibe565df2014-04-28 17:51:23 +0200216 public void setOnHeightChangedListener(OnHeightChangedListener listener) {
217 mOnHeightChangedListener = listener;
218 }
219
220 /**
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200221 * @return Whether we can expand this views content.
Jorim Jaggibe565df2014-04-28 17:51:23 +0200222 */
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200223 public boolean isContentExpandable() {
224 return false;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200225 }
226
Jorim Jaggi9cbadd32014-05-01 20:18:31 +0200227 public void notifyHeightChanged() {
228 if (mOnHeightChangedListener != null) {
229 mOnHeightChangedListener.onHeightChanged(this);
230 }
231 }
232
Selim Cinekc27437b2014-05-14 10:23:33 +0200233 public boolean isTransparent() {
234 return false;
235 }
236
Jorim Jaggibe565df2014-04-28 17:51:23 +0200237 /**
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200238 * Perform a remove animation on this view.
239 *
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200240 * @param duration The duration of the remove animation.
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200241 * @param translationDirection The direction value from [-1 ... 1] indicating in which the
242 * animation should be performed. A value of -1 means that The
243 * remove animation should be performed upwards,
244 * such that the child appears to be going away to the top. 1
245 * Should mean the opposite.
246 * @param onFinishedRunnable A runnable which should be run when the animation is finished.
247 */
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200248 public abstract void performRemoveAnimation(long duration, float translationDirection,
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200249 Runnable onFinishedRunnable);
250
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200251 public abstract void performAddAnimation(long delay, long duration);
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200252
Selim Cinekf54090e2014-06-17 17:24:51 -0700253 public abstract void setScrimAmount(float scrimAmount);
254
Selim Cinek3d2b94bf2014-07-02 22:12:47 +0200255 public void setBelowSpeedBump(boolean below) {
256 }
257
Selim Cinek31094df2014-08-14 19:28:15 +0200258 public void onHeightReset() {
Selim Cineke34c6512014-08-14 11:19:41 +0200259 if (mOnHeightChangedListener != null) {
260 mOnHeightChangedListener.onReset(this);
261 }
Selim Cineka5e211b2014-08-11 17:35:48 +0200262 }
263
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200264 /**
Jorim Jaggibe565df2014-04-28 17:51:23 +0200265 * A listener notifying when {@link #getActualHeight} changes.
266 */
267 public interface OnHeightChangedListener {
Jorim Jaggi30c305c2014-07-01 23:34:41 +0200268
269 /**
270 * @param view the view for which the height changed, or {@code null} if just the top
271 * padding or the padding between the elements changed
272 */
Jorim Jaggibe565df2014-04-28 17:51:23 +0200273 void onHeightChanged(ExpandableView view);
Selim Cineka5e211b2014-08-11 17:35:48 +0200274
275 /**
276 * Called when the view is reset and therefore the height will change abruptly
277 *
278 * @param view The view which was reset.
279 */
280 void onReset(ExpandableView view);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200281 }
282}