blob: e287b7a36d8d2e962c08bd9257bc2067efc103be [file] [log] [blame]
Daniel Sandlerd42497e2011-06-04 00:32:50 -04001/*
2 * Copyright (C) 2011 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.policy;
18
19import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.AnimatorSet;
22import android.animation.ObjectAnimator;
Daniel Sandlerad6352b2011-06-13 11:43:45 -040023import android.animation.TimeAnimator;
Daniel Sandlerd42497e2011-06-04 00:32:50 -040024import android.content.Context;
Michael Jurka07d40462011-07-19 10:54:38 -070025import android.content.res.Configuration;
Daniel Sandlerd42497e2011-06-04 00:32:50 -040026import android.content.res.TypedArray;
Daniel Sandlerd42497e2011-06-04 00:32:50 -040027import android.graphics.Rect;
Daniel Sandlerd42497e2011-06-04 00:32:50 -040028import android.util.AttributeSet;
Michael Jurka07d40462011-07-19 10:54:38 -070029import android.util.Log;
Daniel Sandlerd42497e2011-06-04 00:32:50 -040030import android.util.Slog;
Daniel Sandlerad6352b2011-06-13 11:43:45 -040031import android.view.MotionEvent;
32import android.view.VelocityTracker;
Daniel Sandlerd42497e2011-06-04 00:32:50 -040033import android.view.View;
Michael Jurka07d40462011-07-19 10:54:38 -070034import android.view.ViewConfiguration;
Daniel Sandlerd42497e2011-06-04 00:32:50 -040035import android.view.ViewGroup;
Michael Jurka07d40462011-07-19 10:54:38 -070036
37import com.android.systemui.R;
38import com.android.systemui.SwipeHelper;
Daniel Sandlerd42497e2011-06-04 00:32:50 -040039
40import java.util.HashSet;
41
Michael Jurka07d40462011-07-19 10:54:38 -070042public class NotificationRowLayout extends ViewGroup implements SwipeHelper.Callback {
Daniel Sandlerd42497e2011-06-04 00:32:50 -040043 private static final String TAG = "NotificationRowLayout";
44 private static final boolean DEBUG = false;
Daniel Sandlerad6352b2011-06-13 11:43:45 -040045 private static final boolean SLOW_ANIMATIONS = false; // DEBUG;
Daniel Sandlerd42497e2011-06-04 00:32:50 -040046
47 private static final boolean ANIMATE_LAYOUT = true;
48
Daniel Sandlerad6352b2011-06-13 11:43:45 -040049 private static final int APPEAR_ANIM_LEN = SLOW_ANIMATIONS ? 5000 : 250;
50 private static final int DISAPPEAR_ANIM_LEN = APPEAR_ANIM_LEN;
Daniel Sandlerd42497e2011-06-04 00:32:50 -040051
52 Rect mTmpRect = new Rect();
53 int mNumRows = 0;
54 int mRowHeight = 0;
55 int mHeight = 0;
56
57 HashSet<View> mAppearingViews = new HashSet<View>();
58 HashSet<View> mDisappearingViews = new HashSet<View>();
59
Michael Jurka07d40462011-07-19 10:54:38 -070060 private SwipeHelper mSwipeHelper;
Daniel Sandlerad6352b2011-06-13 11:43:45 -040061
Daniel Sandlerd42497e2011-06-04 00:32:50 -040062 public NotificationRowLayout(Context context, AttributeSet attrs) {
63 this(context, attrs, 0);
64 }
65
66 public NotificationRowLayout(Context context, AttributeSet attrs, int defStyle) {
67 super(context, attrs, defStyle);
68
69 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NotificationRowLayout,
70 defStyle, 0);
71 mRowHeight = a.getDimensionPixelSize(R.styleable.NotificationRowLayout_rowHeight, 0);
72 a.recycle();
73
74 setLayoutTransition(null);
75
76 if (DEBUG) {
77 setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
78 @Override
79 public void onChildViewAdded(View parent, View child) {
80 Slog.d(TAG, "view added: " + child + "; new count: " + getChildCount());
81 }
82 @Override
83 public void onChildViewRemoved(View parent, View child) {
84 Slog.d(TAG, "view removed: " + child + "; new count: " + (getChildCount() - 1));
85 }
86 });
87
88 setBackgroundColor(0x80FF8000);
89 }
90
Michael Jurka07d40462011-07-19 10:54:38 -070091 float densityScale = getResources().getDisplayMetrics().density;
92 float pagingTouchSlop = ViewConfiguration.get(mContext).getScaledPagingTouchSlop();
93 mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, pagingTouchSlop);
Daniel Sandlerd42497e2011-06-04 00:32:50 -040094 }
95
Daniel Sandlerad6352b2011-06-13 11:43:45 -040096 @Override
97 public boolean onInterceptTouchEvent(MotionEvent ev) {
Michael Jurka07d40462011-07-19 10:54:38 -070098 if (DEBUG) Log.v(TAG, "onInterceptTouchEvent()");
99 return mSwipeHelper.onInterceptTouchEvent(ev) ||
100 super.onInterceptTouchEvent(ev);
Daniel Sandlerebce0112011-06-16 16:44:51 -0400101 }
102
Daniel Sandlerad6352b2011-06-13 11:43:45 -0400103 @Override
104 public boolean onTouchEvent(MotionEvent ev) {
Michael Jurka07d40462011-07-19 10:54:38 -0700105 return mSwipeHelper.onTouchEvent(ev) ||
106 super.onTouchEvent(ev);
107 }
Daniel Sandlerad6352b2011-06-13 11:43:45 -0400108
Michael Jurka07d40462011-07-19 10:54:38 -0700109 public boolean canChildBeDismissed(View v) {
110 final View veto = v.findViewById(R.id.veto);
111 return (veto != null && veto.getVisibility() != View.GONE);
112 }
Daniel Sandlerad6352b2011-06-13 11:43:45 -0400113
Michael Jurka07d40462011-07-19 10:54:38 -0700114 public void onChildDismissed(View v) {
115 final View veto = v.findViewById(R.id.veto);
116 if (veto != null && veto.getVisibility() != View.GONE) {
117 veto.performClick();
Daniel Sandlerad6352b2011-06-13 11:43:45 -0400118 }
Michael Jurka07d40462011-07-19 10:54:38 -0700119 }
120
121 public void onBeginDrag(View v) {
122 // We need to prevent the surrounding ScrollView from intercepting us now;
123 // the scroll position will be locked while we swipe
124 requestDisallowInterceptTouchEvent(true);
125 }
126
Peter Ng622a9762011-08-29 10:56:53 -0700127 public void onDragCancelled(View v) {
128 }
129
Michael Jurka07d40462011-07-19 10:54:38 -0700130 public View getChildAtPosition(MotionEvent ev) {
131 // find the view under the pointer, accounting for GONE views
132 final int count = getChildCount();
133 int y = 0;
134 int touchY = (int) ev.getY();
135 int childIdx = 0;
136 View slidingChild;
137 for (; childIdx < count; childIdx++) {
138 slidingChild = getChildAt(childIdx);
139 if (slidingChild.getVisibility() == GONE) {
140 continue;
141 }
142 y += mRowHeight;
143 if (touchY < y) return slidingChild;
144 }
145 return null;
146 }
147
148 public View getChildContentView(View v) {
149 return v;
150 }
151
152 @Override
153 protected void onConfigurationChanged(Configuration newConfig) {
154 super.onConfigurationChanged(newConfig);
155 float densityScale = getResources().getDisplayMetrics().density;
156 mSwipeHelper.setDensityScale(densityScale);
157 float pagingTouchSlop = ViewConfiguration.get(mContext).getScaledPagingTouchSlop();
158 mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
Daniel Sandlerad6352b2011-06-13 11:43:45 -0400159 }
160
Daniel Sandlerd42497e2011-06-04 00:32:50 -0400161 //**
162 @Override
163 public void addView(View child, int index, LayoutParams params) {
164 super.addView(child, index, params);
165
166 final View childF = child;
167
168 if (ANIMATE_LAYOUT) {
169 mAppearingViews.add(child);
170
171 child.setPivotY(0);
Daniel Sandler0761e4c2011-08-11 00:19:49 -0400172 final ObjectAnimator alphaFade = ObjectAnimator.ofFloat(child, "alpha", 0f, 1f);
173 alphaFade.setDuration(APPEAR_ANIM_LEN);
174 alphaFade.addListener(new AnimatorListenerAdapter() {
Daniel Sandlerd42497e2011-06-04 00:32:50 -0400175 @Override
176 public void onAnimationEnd(Animator animation) {
177 mAppearingViews.remove(childF);
Daniel Sandler942d92f2011-07-19 16:41:23 -0400178 requestLayout(); // pick up any final changes in position
Daniel Sandlerd42497e2011-06-04 00:32:50 -0400179 }
180 });
Daniel Sandler0761e4c2011-08-11 00:19:49 -0400181 alphaFade.start();
Daniel Sandlerd42497e2011-06-04 00:32:50 -0400182 requestLayout(); // start the container animation
183 }
184 }
185
186 @Override
187 public void removeView(View child) {
188 final View childF = child;
189 if (ANIMATE_LAYOUT) {
190 if (mAppearingViews.contains(child)) {
191 mAppearingViews.remove(child);
192 }
193 mDisappearingViews.add(child);
194
195 child.setPivotY(0);
Daniel Sandlerad6352b2011-06-13 11:43:45 -0400196
Daniel Sandlerad6352b2011-06-13 11:43:45 -0400197 final ObjectAnimator alphaFade = ObjectAnimator.ofFloat(child, "alpha", 0f);
198 alphaFade.addListener(new AnimatorListenerAdapter() {
Daniel Sandlerd42497e2011-06-04 00:32:50 -0400199 @Override
200 public void onAnimationEnd(Animator animation) {
Daniel Sandlerad6352b2011-06-13 11:43:45 -0400201 if (DEBUG) Slog.d(TAG, "actually removing child: " + childF);
Daniel Sandlerd42497e2011-06-04 00:32:50 -0400202 NotificationRowLayout.super.removeView(childF);
203 childF.setAlpha(1f);
204 mDisappearingViews.remove(childF);
Daniel Sandler942d92f2011-07-19 16:41:23 -0400205 requestLayout(); // pick up any final changes in position
Daniel Sandlerd42497e2011-06-04 00:32:50 -0400206 }
207 });
Daniel Sandlerad6352b2011-06-13 11:43:45 -0400208
Daniel Sandler0761e4c2011-08-11 00:19:49 -0400209 alphaFade.setDuration(DISAPPEAR_ANIM_LEN);
210 alphaFade.start();
Daniel Sandlerd42497e2011-06-04 00:32:50 -0400211 requestLayout(); // start the container animation
212 } else {
213 super.removeView(child);
214 }
215 }
216 //**
217
218 @Override
219 public void onFinishInflate() {
220 super.onFinishInflate();
221 setWillNotDraw(false);
222 }
223
224 @Override
225 public void onDraw(android.graphics.Canvas c) {
226 super.onDraw(c);
227 if (DEBUG) {
Daniel Sandlerad6352b2011-06-13 11:43:45 -0400228 //Slog.d(TAG, "onDraw: canvas height: " + c.getHeight() + "px; measured height: "
229 // + getMeasuredHeight() + "px");
Daniel Sandlerd42497e2011-06-04 00:32:50 -0400230 c.save();
231 c.clipRect(6, 6, c.getWidth() - 6, getMeasuredHeight() - 6,
232 android.graphics.Region.Op.DIFFERENCE);
233 c.drawColor(0xFFFF8000);
234 c.restore();
235 }
236 }
237
238 @Override
239 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
240 final int count = getChildCount();
241
242 // pass 1: count the number of non-GONE views
243 int numRows = 0;
244 for (int i = 0; i < count; i++) {
245 final View child = getChildAt(i);
246 if (child.getVisibility() == GONE) {
247 continue;
248 }
249 if (mDisappearingViews.contains(child)) {
250 continue;
251 }
252 numRows++;
253 }
254 if (numRows != mNumRows) {
255 // uh oh, now you made us go and do work
256
257 final int computedHeight = numRows * mRowHeight;
258 if (DEBUG) {
259 Slog.d(TAG, String.format("rows went from %d to %d, resizing to %dpx",
260 mNumRows, numRows, computedHeight));
261 }
262
263 mNumRows = numRows;
264
265 if (ANIMATE_LAYOUT && isShown()) {
266 ObjectAnimator.ofInt(this, "forcedHeight", computedHeight)
Daniel Sandlerad6352b2011-06-13 11:43:45 -0400267 .setDuration(APPEAR_ANIM_LEN)
Daniel Sandlerd42497e2011-06-04 00:32:50 -0400268 .start();
269 } else {
270 setForcedHeight(computedHeight);
271 }
272 }
273
274 // pass 2: you know, do the measuring
275 final int childWidthMS = widthMeasureSpec;
276 final int childHeightMS = MeasureSpec.makeMeasureSpec(
277 mRowHeight, MeasureSpec.EXACTLY);
278
279 for (int i = 0; i < count; i++) {
280 final View child = getChildAt(i);
281 if (child.getVisibility() == GONE) {
282 continue;
283 }
284
285 child.measure(childWidthMS, childHeightMS);
286 }
287
288 setMeasuredDimension(
289 getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
290 resolveSize(getForcedHeight(), heightMeasureSpec));
291 }
292
293 @Override
294 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
295 final int width = right - left;
296 final int height = bottom - top;
297
Daniel Sandler942d92f2011-07-19 16:41:23 -0400298 if (DEBUG) Slog.d(TAG, "onLayout: height=" + height);
Daniel Sandlerd42497e2011-06-04 00:32:50 -0400299
300 final int count = getChildCount();
301 int y = 0;
302 for (int i = 0; i < count; i++) {
303 final View child = getChildAt(i);
304 if (child.getVisibility() == GONE) {
305 continue;
306 }
Daniel Sandler7c3e39d2011-07-29 16:30:49 -0400307 float alpha = child.getAlpha();
308 if (alpha > 1.0f) {
309 if (DEBUG) {
310 Slog.w(TAG, "alpha=" + alpha + " > 1!!! " + child);
311 }
312 alpha = 1f;
313 }
314 final int thisRowHeight = (int)(alpha * mRowHeight);
Daniel Sandler942d92f2011-07-19 16:41:23 -0400315 if (DEBUG) {
316 Slog.d(TAG, String.format(
317 "laying out child #%d: (0, %d, %d, %d) h=%d",
318 i, y, width, y + thisRowHeight, thisRowHeight));
319 }
320 child.layout(0, y, width, y + thisRowHeight);
Daniel Sandlerd42497e2011-06-04 00:32:50 -0400321 y += thisRowHeight;
322 }
Daniel Sandler942d92f2011-07-19 16:41:23 -0400323 if (DEBUG) {
324 Slog.d(TAG, "onLayout: final y=" + y);
325 }
Daniel Sandlerd42497e2011-06-04 00:32:50 -0400326 }
327
328 public void setForcedHeight(int h) {
Daniel Sandler942d92f2011-07-19 16:41:23 -0400329 if (DEBUG) Slog.d(TAG, "forcedHeight: " + h);
Daniel Sandlerd42497e2011-06-04 00:32:50 -0400330 if (h != mHeight) {
331 mHeight = h;
332 requestLayout();
333 }
334 }
335
336 public int getForcedHeight() {
337 return mHeight;
338 }
339}