NotificationRowLayout.java revision d42497e516521891a9d6ffa0daab75ef016725f5
1/*
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;
23import android.animation.ValueAnimator;
24import android.content.Context;
25import android.content.res.Resources;
26import android.content.res.TypedArray;
27import android.graphics.Canvas;
28import android.graphics.Rect;
29import android.graphics.drawable.Drawable;
30import android.util.AttributeSet;
31import android.util.Slog;
32import android.view.LayoutInflater;
33import android.view.View;
34import android.view.ViewGroup;
35import android.view.animation.AccelerateInterpolator;
36import android.widget.FrameLayout;
37import android.widget.ImageView;
38import android.widget.LinearLayout;
39import android.widget.TextView;
40
41import java.util.HashSet;
42
43import com.android.systemui.R;
44
45public class NotificationRowLayout extends ViewGroup {
46    private static final String TAG = "NotificationRowLayout";
47    private static final boolean DEBUG = false;
48
49    private static final boolean ANIMATE_LAYOUT = true;
50
51    private static final int ANIM_LEN = DEBUG ? 5000 : 250;
52
53    Rect mTmpRect = new Rect();
54    int mNumRows = 0;
55    int mRowHeight = 0;
56    int mHeight = 0;
57
58    HashSet<View> mAppearingViews = new HashSet<View>();
59    HashSet<View> mDisappearingViews = new HashSet<View>();
60
61    public NotificationRowLayout(Context context, AttributeSet attrs) {
62        this(context, attrs, 0);
63    }
64
65    public NotificationRowLayout(Context context, AttributeSet attrs, int defStyle) {
66        super(context, attrs, defStyle);
67
68        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NotificationRowLayout,
69                defStyle, 0);
70        mRowHeight = a.getDimensionPixelSize(R.styleable.NotificationRowLayout_rowHeight, 0);
71        a.recycle();
72
73        setLayoutTransition(null);
74
75        if (DEBUG) {
76            setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
77                @Override
78                public void onChildViewAdded(View parent, View child) {
79                    Slog.d(TAG, "view added: " + child + "; new count: " + getChildCount());
80                }
81                @Override
82                public void onChildViewRemoved(View parent, View child) {
83                    Slog.d(TAG, "view removed: " + child + "; new count: " + (getChildCount() - 1));
84                }
85            });
86
87            setBackgroundColor(0x80FF8000);
88        }
89
90    }
91
92    //**
93    @Override
94    public void addView(View child, int index, LayoutParams params) {
95        super.addView(child, index, params);
96
97        final View childF = child;
98
99        if (ANIMATE_LAYOUT) {
100            mAppearingViews.add(child);
101
102            child.setPivotY(0);
103            AnimatorSet a = new AnimatorSet();
104            a.playTogether(
105                    ObjectAnimator.ofFloat(child, "alpha", 0f, 1f)
106//                    ,ObjectAnimator.ofFloat(child, "scaleY", 0f, 1f)
107            );
108            a.setDuration(ANIM_LEN);
109            a.addListener(new AnimatorListenerAdapter() {
110                @Override
111                public void onAnimationEnd(Animator animation) {
112                    mAppearingViews.remove(childF);
113                }
114            });
115            a.start();
116            requestLayout(); // start the container animation
117        }
118    }
119
120    @Override
121    public void removeView(View child) {
122        final View childF = child;
123        if (ANIMATE_LAYOUT) {
124            if (mAppearingViews.contains(child)) {
125                mAppearingViews.remove(child);
126            }
127            mDisappearingViews.add(child);
128
129            child.setPivotY(0);
130            AnimatorSet a = new AnimatorSet();
131            a.playTogether(
132                    ObjectAnimator.ofFloat(child, "alpha", 0f)
133//                    ,ObjectAnimator.ofFloat(child, "scaleY", 0f)
134                    ,ObjectAnimator.ofFloat(child, "translationX", 300f)
135            );
136            a.setDuration(ANIM_LEN);
137            a.addListener(new AnimatorListenerAdapter() {
138                @Override
139                public void onAnimationEnd(Animator animation) {
140                    NotificationRowLayout.super.removeView(childF);
141                    childF.setAlpha(1f);
142                    mDisappearingViews.remove(childF);
143                }
144            });
145            a.start();
146            requestLayout(); // start the container animation
147        } else {
148            super.removeView(child);
149        }
150    }
151    //**
152
153    @Override
154    public void onFinishInflate() {
155        super.onFinishInflate();
156        setWillNotDraw(false);
157    }
158
159    @Override
160    public void onDraw(android.graphics.Canvas c) {
161        super.onDraw(c);
162        if (DEBUG) {
163            Slog.d(TAG, "onDraw: canvas height: " + c.getHeight() + "px; measured height: "
164                    + getMeasuredHeight() + "px");
165            c.save();
166            c.clipRect(6, 6, c.getWidth() - 6, getMeasuredHeight() - 6,
167                    android.graphics.Region.Op.DIFFERENCE);
168            c.drawColor(0xFFFF8000);
169            c.restore();
170        }
171    }
172
173    @Override
174    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
175        final int count = getChildCount();
176
177        // pass 1: count the number of non-GONE views
178        int numRows = 0;
179        for (int i = 0; i < count; i++) {
180            final View child = getChildAt(i);
181            if (child.getVisibility() == GONE) {
182                continue;
183            }
184            if (mDisappearingViews.contains(child)) {
185                continue;
186            }
187            numRows++;
188        }
189        if (numRows != mNumRows) {
190            // uh oh, now you made us go and do work
191
192            final int computedHeight = numRows * mRowHeight;
193            if (DEBUG) {
194                Slog.d(TAG, String.format("rows went from %d to %d, resizing to %dpx",
195                            mNumRows, numRows, computedHeight));
196            }
197
198            mNumRows = numRows;
199
200            if (ANIMATE_LAYOUT && isShown()) {
201                ObjectAnimator.ofInt(this, "forcedHeight", computedHeight)
202                    .setDuration(ANIM_LEN)
203                    .start();
204            } else {
205                setForcedHeight(computedHeight);
206            }
207        }
208
209        // pass 2: you know, do the measuring
210        final int childWidthMS = widthMeasureSpec;
211        final int childHeightMS = MeasureSpec.makeMeasureSpec(
212                mRowHeight, MeasureSpec.EXACTLY);
213
214        for (int i = 0; i < count; i++) {
215            final View child = getChildAt(i);
216            if (child.getVisibility() == GONE) {
217                continue;
218            }
219
220            child.measure(childWidthMS, childHeightMS);
221        }
222
223        setMeasuredDimension(
224                getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
225                resolveSize(getForcedHeight(), heightMeasureSpec));
226    }
227
228    @Override
229    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
230        final int width = right - left;
231        final int height = bottom - top;
232
233        if (DEBUG) Slog.d(TAG, "onLayout: height=" + height);
234
235        final int count = getChildCount();
236        int y = 0;
237        for (int i = 0; i < count; i++) {
238            final View child = getChildAt(i);
239            if (child.getVisibility() == GONE) {
240                continue;
241            }
242//            final int thisRowHeight = (int)(
243//                ((mAppearingViews.contains(child) || mDisappearingViews.contains(child))
244//                        ? child.getScaleY()
245//                        : 1.0f)
246//                * mRowHeight);
247            final int thisRowHeight = (int)(child.getAlpha() * mRowHeight);
248//            child.layout(0, y, width, y + thisRowHeight);
249            child.layout(0, y, width, y + mRowHeight);
250            y += thisRowHeight;
251        }
252    }
253
254    public void setForcedHeight(int h) {
255        if (DEBUG) Slog.d(TAG, "forcedHeight: " + h);
256        if (h != mHeight) {
257            mHeight = h;
258            requestLayout();
259        }
260    }
261
262    public int getForcedHeight() {
263        return mHeight;
264    }
265}
266