NotificationPanel.java revision 7c270fab75c2f4529e45ea80b31d4017ab516482
1/*
2 * Copyright (C) 2010 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.tablet;
18
19import android.animation.Animator;
20import android.animation.ObjectAnimator;
21import android.animation.ValueAnimator;
22import android.content.Context;
23import android.content.res.Resources;
24import android.graphics.Canvas;
25import android.graphics.Rect;
26import android.graphics.drawable.Drawable;
27import android.util.AttributeSet;
28import android.util.Slog;
29import android.view.LayoutInflater;
30import android.view.View;
31import android.view.ViewGroup;
32import android.view.animation.AccelerateInterpolator;
33import android.widget.FrameLayout;
34import android.widget.ImageView;
35import android.widget.LinearLayout;
36import android.widget.TextView;
37
38import com.android.systemui.R;
39
40public class NotificationPanel extends LinearLayout implements StatusBarPanel,
41        View.OnClickListener {
42    static final String TAG = "NotificationPanel";
43
44    boolean mShowing;
45    View mTitleArea;
46    View mSettingsButton;
47    View mNotificationButton;
48    View mNotificationScroller;
49    ViewGroup mContentFrame;
50    Rect mContentArea;
51    View mSettingsView;
52    ViewGroup mContentParent;
53
54    Choreographer mChoreo = new Choreographer();
55    int mStatusBarHeight;
56    Drawable mBgDrawable;
57    Drawable mGlowDrawable;
58
59    public NotificationPanel(Context context, AttributeSet attrs) {
60        this(context, attrs, 0);
61    }
62
63    public NotificationPanel(Context context, AttributeSet attrs, int defStyle) {
64        super(context, attrs, defStyle);
65
66        final Resources res = context.getResources();
67
68        mStatusBarHeight = res.getDimensionPixelSize(
69                com.android.internal.R.dimen.status_bar_height);
70        mBgDrawable = res.getDrawable(R.drawable.notify_panel_bg_protect);
71        mGlowDrawable = res.getDrawable(R.drawable.notify_glow_back);
72    }
73
74    @Override
75    public void onFinishInflate() {
76        super.onFinishInflate();
77
78        setWillNotDraw(false);
79
80        mContentParent = (ViewGroup)findViewById(R.id.content_parent);
81        mTitleArea = findViewById(R.id.title_area);
82
83        mSettingsButton = (ImageView)findViewById(R.id.settings_button);
84        mSettingsButton.setOnClickListener(this);
85        mNotificationButton = (ImageView)findViewById(R.id.notification_button);
86        mNotificationButton.setOnClickListener(this);
87
88        mNotificationScroller = findViewById(R.id.notificationScroller);
89        mContentFrame = (ViewGroup)findViewById(R.id.content_frame);
90    }
91
92    public void show(boolean show, boolean animate) {
93        if (animate) {
94            if (mShowing != show) {
95                mShowing = show;
96                if (show) {
97                    setVisibility(View.VISIBLE);
98                }
99                mChoreo.startAnimation(show);
100            }
101        } else {
102            mShowing = show;
103            setVisibility(show ? View.VISIBLE : View.GONE);
104            mChoreo.jumpTo(show);
105        }
106    }
107
108    /**
109     * Whether the panel is showing, or, if it's animating, whether it will be
110     * when the animation is done.
111     */
112    public boolean isShowing() {
113        return mShowing;
114    }
115
116    @Override
117    public void onVisibilityChanged(View v, int vis) {
118        super.onVisibilityChanged(v, vis);
119        // when we hide, put back the notifications
120        if (!isShown()) {
121            switchToNotificationMode();
122            mNotificationScroller.scrollTo(0, 0);
123        }
124    }
125
126    /**
127     * We need to be aligned at the bottom.  LinearLayout can't do this, so instead,
128     * let LinearLayout do all the hard work, and then shift everything down to the bottom.
129     */
130    @Override
131    protected void onLayout(boolean changed, int l, int t, int r, int b) {
132        super.onLayout(changed, l, t, r, b);
133        // We know that none of our children are GONE, so don't worry about skipping GONE views.
134        final int N = getChildCount();
135        if (N == 0) {
136            return;
137        }
138        final int allocatedBottom = getChildAt(N-1).getBottom();
139        final int shift = b - allocatedBottom - getPaddingBottom();
140        if (shift <= 0) {
141            return;
142        }
143        for (int i=0; i<N; i++) {
144            final View c = getChildAt(i);
145            c.layout(c.getLeft(), c.getTop() + shift, c.getRight(), c.getBottom() + shift);
146        }
147
148        mChoreo.setPanelHeight(mContentParent.getHeight());
149    }
150
151    @Override
152    public void onSizeChanged(int w, int h, int oldw, int oldh) {
153        super.onSizeChanged(w, h, oldw, oldh);
154        mContentArea = null;
155        mBgDrawable.setBounds(0, 0, w, h-mStatusBarHeight);
156    }
157
158    @Override
159    public void onDraw(Canvas canvas) {
160        int saveCount;
161        final int w = getWidth();
162        final int h = getHeight();
163
164        super.onDraw(canvas);
165
166        // Background protection
167        mBgDrawable.draw(canvas);
168
169        // The panel glow (behind status bar)
170
171        saveCount = canvas.save();
172        canvas.clipRect(0, 0, w, h-mStatusBarHeight);
173        mGlowDrawable.draw(canvas);
174        canvas.restoreToCount(saveCount);
175    }
176
177    public void onClick(View v) {
178        if (v == mSettingsButton) {
179            switchToSettingsMode();
180        } else if (v == mNotificationButton) {
181            switchToNotificationMode();
182        }
183    }
184
185    public void switchToSettingsMode() {
186        removeSettingsView();
187        addSettingsView();
188        mSettingsButton.setVisibility(View.INVISIBLE);
189        mNotificationScroller.setVisibility(View.GONE);
190        mNotificationButton.setVisibility(View.VISIBLE);
191    }
192
193    public void switchToNotificationMode() {
194        removeSettingsView();
195        mSettingsButton.setVisibility(View.VISIBLE);
196        mNotificationScroller.setVisibility(View.VISIBLE);
197        mNotificationButton.setVisibility(View.INVISIBLE);
198    }
199
200    public boolean isInContentArea(int x, int y) {
201        if (mContentArea == null) {
202            mContentArea = new Rect(mContentFrame.getLeft(),
203                    mTitleArea.getTop(),
204                    mContentFrame.getRight(),
205                    mContentFrame.getBottom());
206            offsetDescendantRectToMyCoords(mContentParent, mContentArea);
207        }
208        return mContentArea.contains(x, y);
209    }
210
211    void removeSettingsView() {
212        if (mSettingsView != null) {
213            mContentFrame.removeView(mSettingsView);
214            mSettingsView = null;
215        }
216    }
217
218    void addSettingsView() {
219        LayoutInflater infl = LayoutInflater.from(getContext());
220        mSettingsView = infl.inflate(R.layout.status_bar_settings_view, mContentFrame, false);
221        mContentFrame.addView(mSettingsView);
222    }
223
224    private class Choreographer implements Animator.AnimatorListener {
225        int mBgAlpha;
226        ValueAnimator mBgAnim;
227        int mPanelHeight;
228        int mPanelBottom;
229        ValueAnimator mPositionAnim;
230
231        // should group this into a multi-property animation
232        final int OPEN_DURATION = 200;
233
234        Choreographer() {
235        }
236
237        void createAnimation(boolean visible) {
238            mBgAnim = ObjectAnimator.ofInt(this, "bgAlpha", mBgAlpha, visible ? 255 : 0)
239                    .setDuration(OPEN_DURATION);
240            mBgAnim.addListener(this);
241
242            mPositionAnim = ObjectAnimator.ofInt(this, "panelBottom", mPanelBottom,
243                        visible ? mPanelHeight : 0)
244                    .setDuration(OPEN_DURATION);
245        }
246
247        void startAnimation(boolean visible) {
248            if (mBgAnim == null) {
249                createAnimation(visible);
250                mBgAnim.start();
251                mPositionAnim.start();
252            } else {
253                mBgAnim.reverse();
254                mPositionAnim.reverse();
255            }
256        }
257
258        void jumpTo(boolean visible) {
259            setBgAlpha(visible ? 255 : 0);
260            setPanelBottom(visible ? mPanelHeight : 0);
261        }
262
263        public void setBgAlpha(int alpha) {
264            mBgAlpha = alpha;
265            mBgDrawable.setAlpha((int)(alpha));
266            invalidate();
267        }
268
269        // 0 is closed, the height of the panel is open
270        public void setPanelBottom(int y) {
271            mPanelBottom = y;
272            int translationY = mPanelHeight - y;
273            mContentParent.setTranslationY(translationY);
274
275            final int glowXOffset = 100;
276            final int glowYOffset = 100;
277            int glowX = mContentParent.getLeft() - glowXOffset;
278            int glowY = mContentParent.getTop() - glowYOffset + translationY;
279            mGlowDrawable.setBounds(glowX, glowY, glowX + mGlowDrawable.getIntrinsicWidth(),
280                    glowY + mGlowDrawable.getIntrinsicHeight());
281
282            float alpha;
283            if (mPanelBottom > glowYOffset) {
284                alpha = 1;
285            } else {
286                alpha = ((float)mPanelBottom) / glowYOffset;
287            }
288            mContentParent.setAlpha(alpha);
289            mGlowDrawable.setAlpha((int)(255 * alpha));
290
291            if (false) {
292                Slog.d(TAG, "mPanelBottom=" + mPanelBottom + "translationY=" + translationY
293                        + " alpha=" + alpha + " glowY=" + glowY);
294            }
295        }
296
297        public void setPanelHeight(int h) {
298            mPanelHeight = h;
299            setPanelBottom(mPanelBottom);
300        }
301
302        public void onAnimationCancel(Animator animation) {
303            //Slog.d(TAG, "onAnimationCancel mBgAlpha=" + mBgAlpha);
304        }
305
306        public void onAnimationEnd(Animator animation) {
307            //Slog.d(TAG, "onAnimationEnd mBgAlpha=" + mBgAlpha);
308            if (mBgAlpha == 0) {
309                setVisibility(View.GONE);
310            }
311            mBgAnim = null;
312        }
313
314        public void onAnimationRepeat(Animator animation) {
315        }
316
317        public void onAnimationStart(Animator animation) {
318        }
319    }
320}
321
322