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