blob: 0d3116f119a168004801dad52363c182bd117ddc [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 }
72 child.measure(widthMeasureSpec, childHeightSpec);
73 int childHeight = child.getMeasuredHeight();
74 maxChildHeight = Math.max(maxChildHeight, childHeight);
75 } else {
76 mMatchParentViews.add(child);
77 }
78 }
79 int ownHeight = hasFixedHeight ? ownMaxHeight : maxChildHeight;
80 newHeightSpec = MeasureSpec.makeMeasureSpec(ownHeight, MeasureSpec.EXACTLY);
81 for (View child : mMatchParentViews) {
82 child.measure(widthMeasureSpec, newHeightSpec);
83 }
84 mMatchParentViews.clear();
85 int width = MeasureSpec.getSize(widthMeasureSpec);
86 setMeasuredDimension(width, ownHeight);
Jorim Jaggibe565df2014-04-28 17:51:23 +020087 }
88
89 @Override
Jorim Jaggibe565df2014-04-28 17:51:23 +020090 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Selim Cinek24d7cfa2014-05-20 13:50:57 +020091 super.onLayout(changed, left, top, right, bottom);
Jorim Jaggibe565df2014-04-28 17:51:23 +020092 if (!mActualHeightInitialized && mActualHeight == 0) {
Selim Cinekc27437b2014-05-14 10:23:33 +020093 mActualHeight = getInitialHeight();
Jorim Jaggibe565df2014-04-28 17:51:23 +020094 }
95 mActualHeightInitialized = true;
96 }
97
Selim Cinekc27437b2014-05-14 10:23:33 +020098 protected int getInitialHeight() {
99 return getHeight();
100 }
101
Jorim Jaggi00ebdfe2014-05-02 17:29:56 +0200102 @Override
103 public boolean dispatchTouchEvent(MotionEvent ev) {
104 if (filterMotionEvent(ev)) {
105 return super.dispatchTouchEvent(ev);
106 }
107 return false;
108 }
109
110 private boolean filterMotionEvent(MotionEvent event) {
111 return event.getActionMasked() != MotionEvent.ACTION_DOWN
112 || event.getY() > mClipTopAmount && event.getY() < mActualHeight;
113 }
114
Jorim Jaggibe565df2014-04-28 17:51:23 +0200115 /**
116 * Sets the actual height of this notification. This is different than the laid out
117 * {@link View#getHeight()}, as we want to avoid layouting during scrolling and expanding.
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200118 *
119 * @param actualHeight The height of this notification.
120 * @param notifyListeners Whether the listener should be informed about the change.
Jorim Jaggibe565df2014-04-28 17:51:23 +0200121 */
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200122 public void setActualHeight(int actualHeight, boolean notifyListeners) {
Jorim Jaggibe565df2014-04-28 17:51:23 +0200123 mActualHeight = actualHeight;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200124 if (notifyListeners) {
125 notifyHeightChanged();
126 }
127 }
128
129 public void setActualHeight(int actualHeight) {
130 setActualHeight(actualHeight, true);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200131 }
132
133 /**
134 * See {@link #setActualHeight}.
135 *
Jorim Jaggi9cbadd32014-05-01 20:18:31 +0200136 * @return The current actual height of this notification.
Jorim Jaggibe565df2014-04-28 17:51:23 +0200137 */
138 public int getActualHeight() {
139 return mActualHeight;
140 }
141
142 /**
143 * @return The maximum height of this notification.
144 */
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200145 public int getMaxHeight() {
146 return getHeight();
147 }
148
149 /**
150 * @return The minimum height of this notification.
151 */
152 public int getMinHeight() {
153 return getHeight();
154 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200155
156 /**
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200157 * Sets the notification as dimmed. The default implementation does nothing.
158 *
159 * @param dimmed Whether the notification should be dimmed.
160 * @param fade Whether an animation should be played to change the state.
161 */
162 public void setDimmed(boolean dimmed, boolean fade) {
163 }
164
165 /**
Jorim Jaggi9cbadd32014-05-01 20:18:31 +0200166 * @return The desired notification height.
167 */
168 public int getIntrinsicHeight() {
Selim Cineka5eaa602014-05-12 21:27:47 +0200169 return getHeight();
Jorim Jaggi9cbadd32014-05-01 20:18:31 +0200170 }
171
172 /**
Jorim Jaggibe565df2014-04-28 17:51:23 +0200173 * Sets the amount this view should be clipped from the top. This is used when an expanded
174 * notification is scrolling in the top or bottom stack.
175 *
176 * @param clipTopAmount The amount of pixels this view should be clipped from top.
177 */
178 public void setClipTopAmount(int clipTopAmount) {
179 mClipTopAmount = clipTopAmount;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200180 }
181
Selim Cinekeb973562014-05-02 17:07:49 +0200182 public int getClipTopAmount() {
183 return mClipTopAmount;
184 }
185
Jorim Jaggibe565df2014-04-28 17:51:23 +0200186 public void setOnHeightChangedListener(OnHeightChangedListener listener) {
187 mOnHeightChangedListener = listener;
188 }
189
190 /**
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200191 * @return Whether we can expand this views content.
Jorim Jaggibe565df2014-04-28 17:51:23 +0200192 */
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200193 public boolean isContentExpandable() {
194 return false;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200195 }
196
Jorim Jaggi9cbadd32014-05-01 20:18:31 +0200197 public void notifyHeightChanged() {
198 if (mOnHeightChangedListener != null) {
199 mOnHeightChangedListener.onHeightChanged(this);
200 }
201 }
202
Selim Cinekc27437b2014-05-14 10:23:33 +0200203 public boolean isTransparent() {
204 return false;
205 }
206
Jorim Jaggibe565df2014-04-28 17:51:23 +0200207 /**
208 * A listener notifying when {@link #getActualHeight} changes.
209 */
210 public interface OnHeightChangedListener {
211 void onHeightChanged(ExpandableView view);
212 }
213}