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