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