PhoneStatusBar.java revision f3f2011285e2f5079db2229461e2d9a448cb61ec
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.phone; 18 19import android.app.Service; 20import android.app.ActivityManagerNative; 21import android.app.Dialog; 22import android.app.Notification; 23import android.app.PendingIntent; 24import android.app.Service; 25import android.app.StatusBarManager; 26import android.content.BroadcastReceiver; 27import android.content.Context; 28import android.content.Intent; 29import android.content.IntentFilter; 30import android.content.res.Resources; 31import android.content.res.Configuration; 32import android.graphics.PixelFormat; 33import android.graphics.Rect; 34import android.graphics.drawable.Drawable; 35import android.net.Uri; 36import android.os.Bundle; 37import android.os.IBinder; 38import android.os.RemoteException; 39import android.os.Handler; 40import android.os.Message; 41import android.os.ServiceManager; 42import android.os.SystemClock; 43import android.text.TextUtils; 44import android.util.Slog; 45import android.util.Log; 46import android.view.Display; 47import android.view.Gravity; 48import android.view.IWindowManager; 49import android.view.KeyEvent; 50import android.view.LayoutInflater; 51import android.view.MotionEvent; 52import android.view.Surface; 53import android.view.VelocityTracker; 54import android.view.View; 55import android.view.ViewGroup; 56import android.view.ViewGroup.LayoutParams; 57import android.view.Window; 58import android.view.WindowManager; 59import android.view.WindowManagerImpl; 60import android.view.animation.Animation; 61import android.view.animation.AnimationUtils; 62import android.widget.ImageView; 63import android.widget.LinearLayout; 64import android.widget.RemoteViews; 65import android.widget.ScrollView; 66import android.widget.TextView; 67import android.widget.FrameLayout; 68 69import java.io.FileDescriptor; 70import java.io.PrintWriter; 71import java.util.ArrayList; 72import java.util.HashMap; 73import java.util.Set; 74 75import com.android.internal.statusbar.StatusBarIcon; 76import com.android.internal.statusbar.StatusBarIconList; 77import com.android.internal.statusbar.StatusBarNotification; 78 79import com.android.systemui.R; 80import com.android.systemui.recent.RecentsPanelView; 81import com.android.systemui.statusbar.NotificationData; 82import com.android.systemui.statusbar.StatusBar; 83import com.android.systemui.statusbar.StatusBarIconView; 84import com.android.systemui.statusbar.policy.DateView; 85import com.android.systemui.statusbar.policy.BatteryController; 86import com.android.systemui.statusbar.policy.LocationController; 87import com.android.systemui.statusbar.policy.NetworkController; 88 89public class PhoneStatusBar extends StatusBar { 90 static final String TAG = "PhoneStatusBar"; 91 static final boolean SPEW = false; 92 public static final boolean DEBUG = false; 93 94 public static final String ACTION_STATUSBAR_START 95 = "com.android.internal.policy.statusbar.START"; 96 97 static final int EXPANDED_LEAVE_ALONE = -10000; 98 static final int EXPANDED_FULL_OPEN = -10001; 99 100 private static final int MSG_ANIMATE = 1000; 101 private static final int MSG_ANIMATE_REVEAL = 1001; 102 private static final int MSG_SHOW_INTRUDER = 1002; 103 private static final int MSG_HIDE_INTRUDER = 1003; 104 private static final int MSG_OPEN_RECENTS_PANEL = 1020; 105 private static final int MSG_CLOSE_RECENTS_PANEL = 1021; 106 107 // will likely move to a resource or other tunable param at some point 108 private static final int INTRUDER_ALERT_DECAY_MS = 10000; 109 110 PhoneStatusBarPolicy mIconPolicy; 111 112 // These are no longer handled by the policy, because we need custom strategies for them 113 BatteryController mBatteryController; 114 LocationController mLocationController; 115 NetworkController mNetworkController; 116 117 int mIconSize; 118 Display mDisplay; 119 120 IWindowManager mWindowManager; 121 122 PhoneStatusBarView mStatusBarView; 123 int mPixelFormat; 124 H mHandler = new H(); 125 Object mQueueLock = new Object(); 126 127 // icons 128 LinearLayout mIcons; 129 IconMerger mNotificationIcons; 130 LinearLayout mStatusIcons; 131 132 // expanded notifications 133 Dialog mExpandedDialog; 134 ExpandedView mExpandedView; 135 WindowManager.LayoutParams mExpandedParams; 136 ScrollView mScrollView; 137 View mNotificationLinearLayout; 138 View mExpandedContents; 139 // top bar 140 TextView mNoNotificationsTitle; 141 TextView mClearButton; 142 // drag bar 143 CloseDragHandle mCloseView; 144 // ongoing 145 NotificationData mOngoing = new NotificationData(); 146 TextView mOngoingTitle; 147 ViewGroup mOngoingItems; 148 // latest 149 NotificationData mLatest = new NotificationData(); 150 TextView mLatestTitle; 151 ViewGroup mLatestItems; 152 // position 153 int[] mPositionTmp = new int[2]; 154 boolean mExpanded; 155 boolean mExpandedVisible; 156 157 // the date view 158 DateView mDateView; 159 160 // for immersive activities 161 private View mIntruderAlertView; 162 163 // on-screen navigation buttons 164 private NavigationBarView mNavigationBarView = null; 165 166 // the tracker view 167 TrackingView mTrackingView; 168 WindowManager.LayoutParams mTrackingParams; 169 int mTrackingPosition; // the position of the top of the tracking view. 170 private boolean mPanelSlightlyVisible; 171 172 // ticker 173 private Ticker mTicker; 174 private View mTickerView; 175 private boolean mTicking; 176 177 // Recent applications 178 private RecentsPanelView mRecentsPanel; 179 180 // Tracking finger for opening/closing. 181 int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore 182 boolean mTracking; 183 VelocityTracker mVelocityTracker; 184 185 static final int ANIM_FRAME_DURATION = (1000/60); 186 187 boolean mAnimating; 188 long mCurAnimationTime; 189 float mDisplayHeight; 190 float mAnimY; 191 float mAnimVel; 192 float mAnimAccel; 193 long mAnimLastTime; 194 boolean mAnimatingReveal = false; 195 int mViewDelta; 196 int[] mAbsPos = new int[2]; 197 198 // for disabling the status bar 199 int mDisabled = 0; 200 201 // tracking calls to View.setSystemUiVisibility() 202 int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE; 203 204 private class ExpandedDialog extends Dialog { 205 ExpandedDialog(Context context) { 206 super(context, com.android.internal.R.style.Theme_Light_NoTitleBar); 207 } 208 209 @Override 210 public boolean dispatchKeyEvent(KeyEvent event) { 211 boolean down = event.getAction() == KeyEvent.ACTION_DOWN; 212 switch (event.getKeyCode()) { 213 case KeyEvent.KEYCODE_BACK: 214 if (!down) { 215 animateCollapse(); 216 } 217 return true; 218 } 219 return super.dispatchKeyEvent(event); 220 } 221 } 222 223 @Override 224 public void start() { 225 mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE)) 226 .getDefaultDisplay(); 227 228 mWindowManager = IWindowManager.Stub.asInterface( 229 ServiceManager.getService(Context.WINDOW_SERVICE)); 230 231 super.start(); 232 233 addNavigationBar(); 234 235 //addIntruderView(); 236 237 // Lastly, call to the icon policy to install/update all the icons. 238 mIconPolicy = new PhoneStatusBarPolicy(mContext); 239 } 240 241 // ================================================================================ 242 // Constructing the view 243 // ================================================================================ 244 protected View makeStatusBarView() { 245 final Context context = mContext; 246 247 Resources res = context.getResources(); 248 249 mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size); 250 251 ExpandedView expanded = (ExpandedView)View.inflate(context, 252 R.layout.status_bar_expanded, null); 253 expanded.mService = this; 254 255 mIntruderAlertView = View.inflate(context, R.layout.intruder_alert, null); 256 mIntruderAlertView.setVisibility(View.GONE); 257 mIntruderAlertView.setClickable(true); 258 259 PhoneStatusBarView sb = (PhoneStatusBarView)View.inflate(context, 260 R.layout.status_bar, null); 261 sb.mService = this; 262 mStatusBarView = sb; 263 264 try { 265 boolean showNav = res.getBoolean(com.android.internal.R.bool.config_showNavigationBar); 266 if (showNav) { 267 mNavigationBarView = 268 (NavigationBarView) View.inflate(context, R.layout.navigation_bar, null); 269 270 sb.setOnSystemUiVisibilityChangeListener( 271 new View.OnSystemUiVisibilityChangeListener() { 272 @Override 273 public void onSystemUiVisibilityChange(int visibility) { 274 if (DEBUG) { 275 Slog.d(TAG, "systemUi: " + visibility); 276 } 277 boolean hide = (0 != (visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)); 278 mNavigationBarView.setHidden(hide); 279 } 280 }); 281 } 282 } catch (Resources.NotFoundException ex) { 283 // no nav bar for you 284 } 285 286 // figure out which pixel-format to use for the status bar. 287 mPixelFormat = PixelFormat.TRANSLUCENT; 288 Drawable bg = sb.getBackground(); 289 if (bg != null) { 290 mPixelFormat = bg.getOpacity(); 291 } 292 293 mStatusIcons = (LinearLayout)sb.findViewById(R.id.statusIcons); 294 mNotificationIcons = (IconMerger)sb.findViewById(R.id.notificationIcons); 295 mIcons = (LinearLayout)sb.findViewById(R.id.icons); 296 mTickerView = sb.findViewById(R.id.ticker); 297 mDateView = (DateView)sb.findViewById(R.id.date); 298 299 mExpandedDialog = new ExpandedDialog(context); 300 mExpandedView = expanded; 301 mExpandedContents = expanded.findViewById(R.id.notificationLinearLayout); 302 mOngoingTitle = (TextView)expanded.findViewById(R.id.ongoingTitle); 303 mOngoingItems = (ViewGroup)expanded.findViewById(R.id.ongoingItems); 304 mLatestTitle = (TextView)expanded.findViewById(R.id.latestTitle); 305 mLatestItems = (ViewGroup)expanded.findViewById(R.id.latestItems); 306 mNoNotificationsTitle = (TextView)expanded.findViewById(R.id.noNotificationsTitle); 307 mClearButton = (TextView)expanded.findViewById(R.id.clear_all_button); 308 mClearButton.setOnClickListener(mClearButtonListener); 309 mScrollView = (ScrollView)expanded.findViewById(R.id.scroll); 310 mNotificationLinearLayout = expanded.findViewById(R.id.notificationLinearLayout); 311 312 mOngoingTitle.setVisibility(View.GONE); 313 mLatestTitle.setVisibility(View.GONE); 314 315 mTicker = new MyTicker(context, sb); 316 317 TickerView tickerView = (TickerView)sb.findViewById(R.id.tickerText); 318 tickerView.mTicker = mTicker; 319 320 mTrackingView = (TrackingView)View.inflate(context, R.layout.status_bar_tracking, null); 321 mTrackingView.mService = this; 322 mCloseView = (CloseDragHandle)mTrackingView.findViewById(R.id.close); 323 mCloseView.mService = this; 324 325 mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore); 326 327 // set the inital view visibility 328 setAreThereNotifications(); 329 mDateView.setVisibility(View.INVISIBLE); 330 331 // Other icons 332 mLocationController = new LocationController(mContext); // will post a notification 333 mBatteryController = new BatteryController(mContext); 334 mBatteryController.addIconView((ImageView)sb.findViewById(R.id.battery)); 335 mNetworkController = new NetworkController(mContext); 336 final ImageView comboRSSI = 337 (ImageView)sb.findViewById(R.id.network_signal); 338 if (comboRSSI != null) { 339 mNetworkController.addCombinedSignalIconView(comboRSSI); 340 } 341 final ImageView mobileRSSI = 342 (ImageView)sb.findViewById(R.id.mobile_signal); 343 if (mobileRSSI != null) { 344 mNetworkController.addPhoneSignalIconView(mobileRSSI); 345 } 346 final ImageView wifiRSSI = 347 (ImageView)sb.findViewById(R.id.wifi_signal); 348 if (wifiRSSI != null) { 349 mNetworkController.addWifiIconView(wifiRSSI); 350 } 351 mNetworkController.addDataTypeIconView( 352 (ImageView)sb.findViewById(R.id.network_type)); 353 mNetworkController.addDataDirectionOverlayIconView( 354 (ImageView)sb.findViewById(R.id.network_direction)); 355 mNetworkController.setStackedMode(true); 356 357 // Recents Panel 358 updateRecentsPanel(); 359 360 // receive broadcasts 361 IntentFilter filter = new IntentFilter(); 362 filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); 363 filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 364 filter.addAction(Intent.ACTION_SCREEN_OFF); 365 context.registerReceiver(mBroadcastReceiver, filter); 366 367 return sb; 368 } 369 370 protected WindowManager.LayoutParams getRecentsLayoutParams(LayoutParams layoutParams) { 371 boolean translucent = false; 372 WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 373 layoutParams.width, 374 layoutParams.height, 375 WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, 376 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 377 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM 378 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH 379 | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, 380 (translucent ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT)); 381 lp.gravity = Gravity.BOTTOM | Gravity.LEFT; 382 lp.setTitle("RecentsPanel"); 383 lp.windowAnimations = R.style.Animation_RecentPanel; 384 lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED 385 | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; 386 return lp; 387 } 388 389 protected void updateRecentsPanel() { 390 // Recents Panel 391 boolean visible = false; 392 if (mRecentsPanel != null) { 393 visible = mRecentsPanel.getVisibility() == View.VISIBLE; 394 WindowManagerImpl.getDefault().removeView(mRecentsPanel); 395 } 396 397 // Provide RecentsPanelView with a temporary parent to allow layout params to work. 398 LinearLayout tmpRoot = new LinearLayout(mContext); 399 mRecentsPanel = (RecentsPanelView) LayoutInflater.from(mContext).inflate( 400 R.layout.status_bar_recent_panel, tmpRoot, false); 401 402 mRecentsPanel.setOnTouchListener(new TouchOutsideListener(MSG_CLOSE_RECENTS_PANEL, 403 mRecentsPanel)); 404 mRecentsPanel.setVisibility(View.GONE); 405 WindowManager.LayoutParams lp = getRecentsLayoutParams(mRecentsPanel.getLayoutParams()); 406 407 WindowManagerImpl.getDefault().addView(mRecentsPanel, lp); 408 mRecentsPanel.setBar(this); 409 if (visible) { 410 // need to set visibility to View.GONE earlier since that 411 // triggers refreshing application list 412 mRecentsPanel.setVisibility(View.VISIBLE); 413 mRecentsPanel.show(true, false); 414 } 415 416 } 417 418 protected int getStatusBarGravity() { 419 return Gravity.TOP | Gravity.FILL_HORIZONTAL; 420 } 421 422 public int getStatusBarHeight() { 423 final Resources res = mContext.getResources(); 424 return res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); 425 } 426 427 private View.OnClickListener mRecentsClickListener = new View.OnClickListener() { 428 public void onClick(View v) { 429 toggleRecentApps(); 430 } 431 }; 432 433 // For small-screen devices (read: phones) that lack hardware navigation buttons 434 private void addNavigationBar() { 435 if (mNavigationBarView == null) return; 436 437 mNavigationBarView.reorient(); 438 439 mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener); 440 441 WindowManagerImpl.getDefault().addView( 442 mNavigationBarView, getNavigationBarLayoutParams()); 443 } 444 445 private void repositionNavigationBar() { 446 if (mNavigationBarView == null) return; 447 448 mNavigationBarView.reorient(); 449 450 mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener); 451 452 WindowManagerImpl.getDefault().updateViewLayout( 453 mNavigationBarView, getNavigationBarLayoutParams()); 454 } 455 456 private WindowManager.LayoutParams getNavigationBarLayoutParams() { 457 final int rotation = mDisplay.getRotation(); 458 final boolean sideways = 459 (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270); 460 461 final Resources res = mContext.getResources(); 462 final int size = res.getDimensionPixelSize(R.dimen.navigation_bar_size); 463 464 WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 465 sideways ? size : ViewGroup.LayoutParams.MATCH_PARENT, 466 sideways ? ViewGroup.LayoutParams.MATCH_PARENT : size, 467 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, 468 0 469 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 470 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 471 | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING 472 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 473 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH 474 | WindowManager.LayoutParams.FLAG_SLIPPERY, 475 PixelFormat.TRANSLUCENT); 476 477 lp.setTitle("NavigationBar"); 478 switch (rotation) { 479 case Surface.ROTATION_90: 480 // device has been turned 90deg counter-clockwise 481 lp.gravity = Gravity.RIGHT | Gravity.FILL_VERTICAL; 482 break; 483 case Surface.ROTATION_270: 484 // device has been turned 90deg clockwise 485 lp.gravity = (NavigationBarView.NAVBAR_ALWAYS_AT_RIGHT ? Gravity.RIGHT 486 : Gravity.LEFT) 487 | Gravity.FILL_VERTICAL; 488 break; 489 default: 490 lp.gravity = Gravity.BOTTOM | Gravity.FILL_HORIZONTAL; 491 break; 492 } 493 lp.windowAnimations = 0; 494 495 return lp; 496 } 497 498 private void addIntruderView() { 499 final int height = getStatusBarHeight(); 500 501 WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 502 ViewGroup.LayoutParams.MATCH_PARENT, 503 ViewGroup.LayoutParams.WRAP_CONTENT, 504 WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL, 505 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 506 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 507 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 508 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 509 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM 510 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, 511 PixelFormat.TRANSLUCENT); 512 lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; 513 lp.y += height * 1.5; // FIXME 514 lp.setTitle("IntruderAlert"); 515 lp.windowAnimations = com.android.internal.R.style.Animation_StatusBar_IntruderAlert; 516 517 WindowManagerImpl.getDefault().addView(mIntruderAlertView, lp); 518 } 519 520 public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) { 521 if (SPEW) Slog.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex 522 + " icon=" + icon); 523 StatusBarIconView view = new StatusBarIconView(mContext, slot, null); 524 view.set(icon); 525 mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconSize, mIconSize)); 526 } 527 528 public void updateIcon(String slot, int index, int viewIndex, 529 StatusBarIcon old, StatusBarIcon icon) { 530 if (SPEW) Slog.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex 531 + " old=" + old + " icon=" + icon); 532 StatusBarIconView view = (StatusBarIconView)mStatusIcons.getChildAt(viewIndex); 533 view.set(icon); 534 } 535 536 public void removeIcon(String slot, int index, int viewIndex) { 537 if (SPEW) Slog.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex); 538 mStatusIcons.removeViewAt(viewIndex); 539 } 540 541 public void addNotification(IBinder key, StatusBarNotification notification) { 542 StatusBarIconView iconView = addNotificationViews(key, notification); 543 if (iconView == null) return; 544 545 boolean immersive = false; 546 try { 547 immersive = ActivityManagerNative.getDefault().isTopActivityImmersive(); 548 Slog.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive")); 549 } catch (RemoteException ex) { 550 } 551 if (immersive) { 552 if ((notification.notification.flags & Notification.FLAG_HIGH_PRIORITY) != 0) { 553 Slog.d(TAG, "Presenting high-priority notification in immersive activity"); 554 // special new transient ticker mode 555 // 1. Populate mIntruderAlertView 556 557 ImageView alertIcon = (ImageView) mIntruderAlertView.findViewById(R.id.alertIcon); 558 TextView alertText = (TextView) mIntruderAlertView.findViewById(R.id.alertText); 559 alertIcon.setImageDrawable(StatusBarIconView.getIcon( 560 alertIcon.getContext(), 561 iconView.getStatusBarIcon())); 562 alertText.setText(notification.notification.tickerText); 563 564 View button = mIntruderAlertView.findViewById(R.id.intruder_alert_content); 565 button.setOnClickListener( 566 new Launcher(notification.notification.contentIntent, 567 notification.pkg, notification.tag, notification.id)); 568 569 // 2. Animate mIntruderAlertView in 570 mHandler.sendEmptyMessage(MSG_SHOW_INTRUDER); 571 572 // 3. Set alarm to age the notification off (TODO) 573 mHandler.removeMessages(MSG_HIDE_INTRUDER); 574 mHandler.sendEmptyMessageDelayed(MSG_HIDE_INTRUDER, INTRUDER_ALERT_DECAY_MS); 575 } 576 } else if (notification.notification.fullScreenIntent != null) { 577 // not immersive & a full-screen alert should be shown 578 Slog.d(TAG, "Notification has fullScreenIntent and activity is not immersive;" 579 + " sending fullScreenIntent"); 580 try { 581 notification.notification.fullScreenIntent.send(); 582 } catch (PendingIntent.CanceledException e) { 583 } 584 } else { 585 // usual case: status bar visible & not immersive 586 587 // show the ticker 588 tick(notification); 589 } 590 591 // Recalculate the position of the sliding windows and the titles. 592 setAreThereNotifications(); 593 updateExpandedViewPos(EXPANDED_LEAVE_ALONE); 594 } 595 596 public void updateNotification(IBinder key, StatusBarNotification notification) { 597 Slog.d(TAG, "updateNotification key=" + key + " notification=" + notification); 598 599 NotificationData oldList; 600 NotificationData.Entry oldEntry = mOngoing.findByKey(key); 601 if (oldEntry != null) { 602 oldList = mOngoing; 603 } else { 604 oldEntry = mLatest.findByKey(key); 605 if (oldEntry == null) { 606 Slog.w(TAG, "updateNotification for unknown key: " + key); 607 return; 608 } 609 oldList = mLatest; 610 } 611 final StatusBarNotification oldNotification = oldEntry.notification; 612 final RemoteViews oldContentView = oldNotification.notification.contentView; 613 614 final RemoteViews contentView = notification.notification.contentView; 615 616 if (DEBUG) { 617 Slog.d(TAG, "old notification: when=" + oldNotification.notification.when 618 + " ongoing=" + oldNotification.isOngoing() 619 + " expanded=" + oldEntry.expanded 620 + " contentView=" + oldContentView 621 + " rowParent=" + oldEntry.row.getParent()); 622 Slog.d(TAG, "new notification: when=" + notification.notification.when 623 + " ongoing=" + oldNotification.isOngoing() 624 + " contentView=" + contentView); 625 } 626 627 628 // Can we just reapply the RemoteViews in place? If when didn't change, the order 629 // didn't change. 630 boolean contentsUnchanged = oldEntry.expanded != null 631 && contentView != null && oldContentView != null 632 && contentView.getPackage() != null 633 && oldContentView.getPackage() != null 634 && oldContentView.getPackage().equals(contentView.getPackage()) 635 && oldContentView.getLayoutId() == contentView.getLayoutId(); 636 ViewGroup rowParent = (ViewGroup) oldEntry.row.getParent(); 637 boolean orderUnchanged = notification.notification.when==oldNotification.notification.when 638 && notification.priority == oldNotification.priority; 639 // priority now encompasses isOngoing() 640 boolean isLastAnyway = rowParent.indexOfChild(oldEntry.row) == rowParent.getChildCount()-1; 641 if (contentsUnchanged && (orderUnchanged || isLastAnyway)) { 642 if (DEBUG) Slog.d(TAG, "reusing notification for key: " + key); 643 oldEntry.notification = notification; 644 try { 645 // Reapply the RemoteViews 646 contentView.reapply(mContext, oldEntry.content); 647 // update the contentIntent 648 final PendingIntent contentIntent = notification.notification.contentIntent; 649 if (contentIntent != null) { 650 oldEntry.content.setOnClickListener(new Launcher(contentIntent, 651 notification.pkg, notification.tag, notification.id)); 652 } else { 653 oldEntry.content.setOnClickListener(null); 654 } 655 // Update the icon. 656 final StatusBarIcon ic = new StatusBarIcon(notification.pkg, 657 notification.notification.icon, notification.notification.iconLevel, 658 notification.notification.number, notification.notification.tickerText); 659 if (!oldEntry.icon.set(ic)) { 660 handleNotificationError(key, notification, "Couldn't update icon: " + ic); 661 return; 662 } 663 // Update the large icon 664 if (notification.notification.largeIcon != null) { 665 oldEntry.largeIcon.setImageBitmap(notification.notification.largeIcon); 666 } else { 667 oldEntry.largeIcon.getLayoutParams().width = 0; 668 oldEntry.largeIcon.setVisibility(View.INVISIBLE); 669 } 670 671 } 672 catch (RuntimeException e) { 673 // It failed to add cleanly. Log, and remove the view from the panel. 674 Slog.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e); 675 removeNotificationViews(key); 676 addNotificationViews(key, notification); 677 } 678 } else { 679 if (SPEW) Slog.d(TAG, "not reusing notification"); 680 removeNotificationViews(key); 681 addNotificationViews(key, notification); 682 } 683 684 // Restart the ticker if it's still running 685 if (notification.notification.tickerText != null 686 && !TextUtils.equals(notification.notification.tickerText, 687 oldEntry.notification.notification.tickerText)) { 688 tick(notification); 689 } 690 691 // Recalculate the position of the sliding windows and the titles. 692 setAreThereNotifications(); 693 updateExpandedViewPos(EXPANDED_LEAVE_ALONE); 694 } 695 696 public void removeNotification(IBinder key) { 697 if (SPEW) Slog.d(TAG, "removeNotification key=" + key); 698 StatusBarNotification old = removeNotificationViews(key); 699 700 if (old != null) { 701 // Cancel the ticker if it's still running 702 mTicker.removeEntry(old); 703 704 // Recalculate the position of the sliding windows and the titles. 705 setAreThereNotifications(); 706 updateExpandedViewPos(EXPANDED_LEAVE_ALONE); 707 } 708 } 709 710 private int chooseIconIndex(boolean isOngoing, int viewIndex) { 711 final int latestSize = mLatest.size(); 712 if (isOngoing) { 713 return latestSize + (mOngoing.size() - viewIndex); 714 } else { 715 return latestSize - viewIndex; 716 } 717 } 718 719 @Override 720 protected void onConfigurationChanged(Configuration newConfig) { 721 updateRecentsPanel(); 722 } 723 724 725 View[] makeNotificationView(StatusBarNotification notification, ViewGroup parent) { 726 Notification n = notification.notification; 727 RemoteViews remoteViews = n.contentView; 728 if (remoteViews == null) { 729 return null; 730 } 731 732 // create the row view 733 LayoutInflater inflater = (LayoutInflater)mContext.getSystemService( 734 Context.LAYOUT_INFLATER_SERVICE); 735 View row = inflater.inflate(R.layout.status_bar_notification_row, parent, false); 736 737 // wire up the veto button 738 View vetoButton = row.findViewById(R.id.veto); 739 if (notification.isClearable()) { 740 final String _pkg = notification.pkg; 741 final String _tag = notification.tag; 742 final int _id = notification.id; 743 vetoButton.setOnClickListener(new View.OnClickListener() { 744 public void onClick(View v) { 745 try { 746 mBarService.onNotificationClear(_pkg, _tag, _id); 747 } catch (RemoteException ex) { 748 // system process is dead if we're here. 749 } 750 } 751 }); 752 } else { 753 if ((notification.notification.flags & Notification.FLAG_ONGOING_EVENT) == 0) { 754 vetoButton.setVisibility(View.INVISIBLE); 755 } else { 756 vetoButton.setVisibility(View.GONE); 757 } 758 } 759 760 // the large icon 761 ImageView largeIcon = (ImageView)row.findViewById(R.id.large_icon); 762 if (notification.notification.largeIcon != null) { 763 largeIcon.setImageBitmap(notification.notification.largeIcon); 764 } else { 765 largeIcon.getLayoutParams().width = 0; 766 largeIcon.setVisibility(View.INVISIBLE); 767 } 768 769 // bind the click event to the content area 770 ViewGroup content = (ViewGroup)row.findViewById(R.id.content); 771 content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); 772 content.setOnFocusChangeListener(mFocusChangeListener); 773 PendingIntent contentIntent = n.contentIntent; 774 if (contentIntent != null) { 775 content.setOnClickListener(new Launcher(contentIntent, notification.pkg, 776 notification.tag, notification.id)); 777 } else { 778 content.setOnClickListener(null); 779 } 780 781 View expanded = null; 782 Exception exception = null; 783 try { 784 expanded = remoteViews.apply(mContext, content); 785 } 786 catch (RuntimeException e) { 787 exception = e; 788 } 789 if (expanded == null) { 790 String ident = notification.pkg + "/0x" + Integer.toHexString(notification.id); 791 Slog.e(TAG, "couldn't inflate view for notification " + ident, exception); 792 return null; 793 } else { 794 content.addView(expanded); 795 row.setDrawingCacheEnabled(true); 796 } 797 798 return new View[] { row, content, expanded }; 799 } 800 801 StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) { 802 NotificationData list; 803 ViewGroup parent; 804 final boolean isOngoing = notification.isOngoing(); 805 if (isOngoing) { 806 list = mOngoing; 807 parent = mOngoingItems; 808 } else { 809 list = mLatest; 810 parent = mLatestItems; 811 } 812 // Construct the expanded view. 813 final View[] views = makeNotificationView(notification, parent); 814 if (views == null) { 815 handleNotificationError(key, notification, "Couldn't expand RemoteViews for: " 816 + notification); 817 return null; 818 } 819 final View row = views[0]; 820 final View content = views[1]; 821 final View expanded = views[2]; 822 // Construct the icon. 823 final StatusBarIconView iconView = new StatusBarIconView(mContext, 824 notification.pkg + "/0x" + Integer.toHexString(notification.id), 825 notification.notification); 826 final StatusBarIcon ic = new StatusBarIcon(notification.pkg, notification.notification.icon, 827 notification.notification.iconLevel, notification.notification.number, 828 notification.notification.tickerText); 829 if (!iconView.set(ic)) { 830 handleNotificationError(key, notification, "Coulding create icon: " + ic); 831 return null; 832 } 833 // Add the expanded view. 834 final int viewIndex = list.add(key, notification, row, content, expanded, iconView); 835 parent.addView(row, viewIndex); 836 // Add the icon. 837 final int iconIndex = chooseIconIndex(isOngoing, viewIndex); 838 mNotificationIcons.addView(iconView, iconIndex); 839 return iconView; 840 } 841 842 StatusBarNotification removeNotificationViews(IBinder key) { 843 NotificationData.Entry entry = mOngoing.remove(key); 844 if (entry == null) { 845 entry = mLatest.remove(key); 846 if (entry == null) { 847 Slog.w(TAG, "removeNotification for unknown key: " + key); 848 return null; 849 } 850 } 851 // Remove the expanded view. 852 ((ViewGroup)entry.row.getParent()).removeView(entry.row); 853 // Remove the icon. 854 ((ViewGroup)entry.icon.getParent()).removeView(entry.icon); 855 856 return entry.notification; 857 } 858 859 private void setAreThereNotifications() { 860 boolean ongoing = mOngoing.hasVisibleItems(); 861 boolean latest = mLatest.hasVisibleItems(); 862 863 // (no ongoing notifications are clearable) 864 if (mLatest.hasClearableItems()) { 865 mClearButton.setVisibility(View.VISIBLE); 866 } else { 867 mClearButton.setVisibility(View.INVISIBLE); 868 } 869 870 mOngoingTitle.setVisibility(ongoing ? View.VISIBLE : View.GONE); 871 mLatestTitle.setVisibility(latest ? View.VISIBLE : View.GONE); 872 873 if (ongoing || latest) { 874 mNoNotificationsTitle.setVisibility(View.GONE); 875 } else { 876 mNoNotificationsTitle.setVisibility(View.VISIBLE); 877 } 878 } 879 880 881 /** 882 * State is one or more of the DISABLE constants from StatusBarManager. 883 */ 884 public void disable(int state) { 885 final int old = mDisabled; 886 final int diff = state ^ old; 887 mDisabled = state; 888 889 if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) { 890 if ((state & StatusBarManager.DISABLE_EXPAND) != 0) { 891 Slog.d(TAG, "DISABLE_EXPAND: yes"); 892 animateCollapse(); 893 } 894 } 895 if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { 896 if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { 897 Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes"); 898 if (mTicking) { 899 mTicker.halt(); 900 } else { 901 setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out); 902 } 903 } else { 904 Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no"); 905 if (!mExpandedVisible) { 906 setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in); 907 } 908 } 909 } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) { 910 if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) { 911 Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: yes"); 912 mTicker.halt(); 913 } 914 } 915 } 916 917 /** 918 * All changes to the status bar and notifications funnel through here and are batched. 919 */ 920 private class H extends Handler { 921 public void handleMessage(Message m) { 922 switch (m.what) { 923 case MSG_ANIMATE: 924 doAnimation(); 925 break; 926 case MSG_ANIMATE_REVEAL: 927 doRevealAnimation(); 928 break; 929 case MSG_SHOW_INTRUDER: 930 setIntruderAlertVisibility(true); 931 break; 932 case MSG_HIDE_INTRUDER: 933 setIntruderAlertVisibility(false); 934 break; 935 case MSG_OPEN_RECENTS_PANEL: 936 if (DEBUG) Slog.d(TAG, "opening recents panel"); 937 if (mRecentsPanel != null) { 938 disable(StatusBarManager.DISABLE_BACK); 939 mRecentsPanel.setVisibility(View.VISIBLE); 940 mRecentsPanel.show(true, true); 941 } 942 break; 943 case MSG_CLOSE_RECENTS_PANEL: 944 if (DEBUG) Slog.d(TAG, "closing recents panel"); 945 if (mRecentsPanel != null && mRecentsPanel.isShowing()) { 946 disable(StatusBarManager.DISABLE_NONE); 947 mRecentsPanel.show(false, true); 948 } 949 break; 950 } 951 } 952 } 953 954 View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() { 955 public void onFocusChange(View v, boolean hasFocus) { 956 // Because 'v' is a ViewGroup, all its children will be (un)selected 957 // too, which allows marqueeing to work. 958 v.setSelected(hasFocus); 959 } 960 }; 961 962 private void makeExpandedVisible() { 963 if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible); 964 if (mExpandedVisible) { 965 return; 966 } 967 mExpandedVisible = true; 968 visibilityChanged(true); 969 970 updateExpandedViewPos(EXPANDED_LEAVE_ALONE); 971 mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 972 mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; 973 mExpandedDialog.getWindow().setAttributes(mExpandedParams); 974 mExpandedView.requestFocus(View.FOCUS_FORWARD); 975 mTrackingView.setVisibility(View.VISIBLE); 976 977 if (!mTicking) { 978 setDateViewVisibility(true, com.android.internal.R.anim.fade_in); 979 } 980 } 981 982 public void animateExpand() { 983 if (SPEW) Slog.d(TAG, "Animate expand: expanded=" + mExpanded); 984 if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) { 985 return ; 986 } 987 if (mExpanded) { 988 return; 989 } 990 991 prepareTracking(0, true); 992 performFling(0, 2000.0f, true); 993 } 994 995 public void animateCollapse() { 996 animateCollapse(false); 997 } 998 999 public void animateCollapse(boolean excludeRecents) { 1000 if (SPEW) { 1001 Slog.d(TAG, "animateCollapse(): mExpanded=" + mExpanded 1002 + " mExpandedVisible=" + mExpandedVisible 1003 + " mExpanded=" + mExpanded 1004 + " mAnimating=" + mAnimating 1005 + " mAnimY=" + mAnimY 1006 + " mAnimVel=" + mAnimVel); 1007 } 1008 1009 if (!excludeRecents) { 1010 mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL); 1011 mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL); 1012 } 1013 1014 if (!mExpandedVisible) { 1015 return; 1016 } 1017 1018 int y; 1019 if (mAnimating) { 1020 y = (int)mAnimY; 1021 } else { 1022 y = mDisplay.getHeight()-1; 1023 } 1024 // Let the fling think that we're open so it goes in the right direction 1025 // and doesn't try to re-open the windowshade. 1026 mExpanded = true; 1027 prepareTracking(y, false); 1028 performFling(y, -2000.0f, true); 1029 } 1030 1031 void performExpand() { 1032 if (SPEW) Slog.d(TAG, "performExpand: mExpanded=" + mExpanded); 1033 if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) { 1034 return ; 1035 } 1036 if (mExpanded) { 1037 return; 1038 } 1039 1040 mExpanded = true; 1041 makeExpandedVisible(); 1042 updateExpandedViewPos(EXPANDED_FULL_OPEN); 1043 1044 if (false) postStartTracing(); 1045 } 1046 1047 void performCollapse() { 1048 if (SPEW) Slog.d(TAG, "performCollapse: mExpanded=" + mExpanded 1049 + " mExpandedVisible=" + mExpandedVisible); 1050 1051 if (!mExpandedVisible) { 1052 return; 1053 } 1054 mExpandedVisible = false; 1055 visibilityChanged(false); 1056 mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 1057 mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; 1058 mExpandedDialog.getWindow().setAttributes(mExpandedParams); 1059 mTrackingView.setVisibility(View.GONE); 1060 1061 if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) { 1062 setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in); 1063 } 1064 setDateViewVisibility(false, com.android.internal.R.anim.fade_out); 1065 1066 if (!mExpanded) { 1067 return; 1068 } 1069 mExpanded = false; 1070 } 1071 1072 void doAnimation() { 1073 if (mAnimating) { 1074 if (SPEW) Slog.d(TAG, "doAnimation"); 1075 if (SPEW) Slog.d(TAG, "doAnimation before mAnimY=" + mAnimY); 1076 incrementAnim(); 1077 if (SPEW) Slog.d(TAG, "doAnimation after mAnimY=" + mAnimY); 1078 if (mAnimY >= mDisplay.getHeight()-1) { 1079 if (SPEW) Slog.d(TAG, "Animation completed to expanded state."); 1080 mAnimating = false; 1081 updateExpandedViewPos(EXPANDED_FULL_OPEN); 1082 performExpand(); 1083 } 1084 else if (mAnimY < mStatusBarView.getHeight()) { 1085 if (SPEW) Slog.d(TAG, "Animation completed to collapsed state."); 1086 mAnimating = false; 1087 updateExpandedViewPos(0); 1088 performCollapse(); 1089 } 1090 else { 1091 updateExpandedViewPos((int)mAnimY); 1092 mCurAnimationTime += ANIM_FRAME_DURATION; 1093 mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime); 1094 } 1095 } 1096 } 1097 1098 void stopTracking() { 1099 mTracking = false; 1100 mVelocityTracker.recycle(); 1101 mVelocityTracker = null; 1102 } 1103 1104 void incrementAnim() { 1105 long now = SystemClock.uptimeMillis(); 1106 float t = ((float)(now - mAnimLastTime)) / 1000; // ms -> s 1107 final float y = mAnimY; 1108 final float v = mAnimVel; // px/s 1109 final float a = mAnimAccel; // px/s/s 1110 mAnimY = y + (v*t) + (0.5f*a*t*t); // px 1111 mAnimVel = v + (a*t); // px/s 1112 mAnimLastTime = now; // ms 1113 //Slog.d(TAG, "y=" + y + " v=" + v + " a=" + a + " t=" + t + " mAnimY=" + mAnimY 1114 // + " mAnimAccel=" + mAnimAccel); 1115 } 1116 1117 void doRevealAnimation() { 1118 final int h = mCloseView.getHeight() + mStatusBarView.getHeight(); 1119 if (mAnimatingReveal && mAnimating && mAnimY < h) { 1120 incrementAnim(); 1121 if (mAnimY >= h) { 1122 mAnimY = h; 1123 updateExpandedViewPos((int)mAnimY); 1124 } else { 1125 updateExpandedViewPos((int)mAnimY); 1126 mCurAnimationTime += ANIM_FRAME_DURATION; 1127 mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL), 1128 mCurAnimationTime); 1129 } 1130 } 1131 } 1132 1133 void prepareTracking(int y, boolean opening) { 1134 mTracking = true; 1135 mVelocityTracker = VelocityTracker.obtain(); 1136 if (opening) { 1137 mAnimAccel = 2000.0f; 1138 mAnimVel = 200; 1139 mAnimY = mStatusBarView.getHeight(); 1140 updateExpandedViewPos((int)mAnimY); 1141 mAnimating = true; 1142 mAnimatingReveal = true; 1143 mHandler.removeMessages(MSG_ANIMATE); 1144 mHandler.removeMessages(MSG_ANIMATE_REVEAL); 1145 long now = SystemClock.uptimeMillis(); 1146 mAnimLastTime = now; 1147 mCurAnimationTime = now + ANIM_FRAME_DURATION; 1148 mAnimating = true; 1149 mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL), 1150 mCurAnimationTime); 1151 makeExpandedVisible(); 1152 } else { 1153 // it's open, close it? 1154 if (mAnimating) { 1155 mAnimating = false; 1156 mHandler.removeMessages(MSG_ANIMATE); 1157 } 1158 updateExpandedViewPos(y + mViewDelta); 1159 } 1160 } 1161 1162 void performFling(int y, float vel, boolean always) { 1163 mAnimatingReveal = false; 1164 mDisplayHeight = mDisplay.getHeight(); 1165 1166 mAnimY = y; 1167 mAnimVel = vel; 1168 1169 //Slog.d(TAG, "starting with mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel); 1170 1171 if (mExpanded) { 1172 if (!always && ( 1173 vel > 200.0f 1174 || (y > (mDisplayHeight-25) && vel > -200.0f))) { 1175 // We are expanded, but they didn't move sufficiently to cause 1176 // us to retract. Animate back to the expanded position. 1177 mAnimAccel = 2000.0f; 1178 if (vel < 0) { 1179 mAnimVel = 0; 1180 } 1181 } 1182 else { 1183 // We are expanded and are now going to animate away. 1184 mAnimAccel = -2000.0f; 1185 if (vel > 0) { 1186 mAnimVel = 0; 1187 } 1188 } 1189 } else { 1190 if (always || ( 1191 vel > 200.0f 1192 || (y > (mDisplayHeight/2) && vel > -200.0f))) { 1193 // We are collapsed, and they moved enough to allow us to 1194 // expand. Animate in the notifications. 1195 mAnimAccel = 2000.0f; 1196 if (vel < 0) { 1197 mAnimVel = 0; 1198 } 1199 } 1200 else { 1201 // We are collapsed, but they didn't move sufficiently to cause 1202 // us to retract. Animate back to the collapsed position. 1203 mAnimAccel = -2000.0f; 1204 if (vel > 0) { 1205 mAnimVel = 0; 1206 } 1207 } 1208 } 1209 //Slog.d(TAG, "mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel 1210 // + " mAnimAccel=" + mAnimAccel); 1211 1212 long now = SystemClock.uptimeMillis(); 1213 mAnimLastTime = now; 1214 mCurAnimationTime = now + ANIM_FRAME_DURATION; 1215 mAnimating = true; 1216 mHandler.removeMessages(MSG_ANIMATE); 1217 mHandler.removeMessages(MSG_ANIMATE_REVEAL); 1218 mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime); 1219 stopTracking(); 1220 } 1221 1222 boolean interceptTouchEvent(MotionEvent event) { 1223 if (SPEW) { 1224 Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled=" 1225 + mDisabled); 1226 } 1227 1228 if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) { 1229 return false; 1230 } 1231 1232 final int statusBarSize = mStatusBarView.getHeight(); 1233 final int hitSize = statusBarSize*2; 1234 if (event.getAction() == MotionEvent.ACTION_DOWN) { 1235 final int y = (int)event.getRawY(); 1236 1237 if (!mExpanded) { 1238 mViewDelta = statusBarSize - y; 1239 } else { 1240 mTrackingView.getLocationOnScreen(mAbsPos); 1241 mViewDelta = mAbsPos[1] + mTrackingView.getHeight() - y; 1242 } 1243 if ((!mExpanded && y < hitSize) || 1244 (mExpanded && y > (mDisplay.getHeight()-hitSize))) { 1245 1246 // We drop events at the edge of the screen to make the windowshade come 1247 // down by accident less, especially when pushing open a device with a keyboard 1248 // that rotates (like g1 and droid) 1249 int x = (int)event.getRawX(); 1250 final int edgeBorder = mEdgeBorder; 1251 if (x >= edgeBorder && x < mDisplay.getWidth() - edgeBorder) { 1252 prepareTracking(y, !mExpanded);// opening if we're not already fully visible 1253 mVelocityTracker.addMovement(event); 1254 } 1255 } 1256 } else if (mTracking) { 1257 mVelocityTracker.addMovement(event); 1258 final int minY = statusBarSize + mCloseView.getHeight(); 1259 if (event.getAction() == MotionEvent.ACTION_MOVE) { 1260 int y = (int)event.getRawY(); 1261 if (mAnimatingReveal && y < minY) { 1262 // nothing 1263 } else { 1264 mAnimatingReveal = false; 1265 updateExpandedViewPos(y + mViewDelta); 1266 } 1267 } else if (event.getAction() == MotionEvent.ACTION_UP) { 1268 mVelocityTracker.computeCurrentVelocity(1000); 1269 1270 float yVel = mVelocityTracker.getYVelocity(); 1271 boolean negative = yVel < 0; 1272 1273 float xVel = mVelocityTracker.getXVelocity(); 1274 if (xVel < 0) { 1275 xVel = -xVel; 1276 } 1277 if (xVel > 150.0f) { 1278 xVel = 150.0f; // limit how much we care about the x axis 1279 } 1280 1281 float vel = (float)Math.hypot(yVel, xVel); 1282 if (negative) { 1283 vel = -vel; 1284 } 1285 1286 performFling((int)event.getRawY(), vel, false); 1287 } 1288 1289 } 1290 return false; 1291 } 1292 1293 @Override // CommandQueue 1294 public void setSystemUiVisibility(int vis) { 1295 if (vis != mSystemUiVisibility) { 1296 mSystemUiVisibility = vis; 1297 1298 if (0 != (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE)) { 1299 animateCollapse(); 1300 } 1301 1302 notifyUiVisibilityChanged(); 1303 } 1304 } 1305 1306 public void setLightsOn(boolean on) { 1307 Log.v(TAG, "setLightsOn(" + on + ")"); 1308 if (on) { 1309 setSystemUiVisibility(mSystemUiVisibility & ~View.SYSTEM_UI_FLAG_LOW_PROFILE); 1310 } else { 1311 setSystemUiVisibility(mSystemUiVisibility | View.SYSTEM_UI_FLAG_LOW_PROFILE); 1312 } 1313 } 1314 1315 private void notifyUiVisibilityChanged() { 1316 try { 1317 mWindowManager.statusBarVisibilityChanged(mSystemUiVisibility); 1318 } catch (RemoteException ex) { 1319 } 1320 } 1321 1322 public void topAppWindowChanged(boolean showMenu) { 1323 if (DEBUG) { 1324 Slog.d(TAG, (showMenu?"showing":"hiding") + " the MENU button"); 1325 } 1326 if (mNavigationBarView != null) { 1327 mNavigationBarView.getMenuButton().setVisibility(showMenu 1328 ? View.VISIBLE : View.INVISIBLE); 1329 } 1330 1331 // See above re: lights-out policy for legacy apps. 1332 if (showMenu) setLightsOn(true); 1333 } 1334 1335 // Not supported 1336 public void setImeWindowStatus(IBinder token, int vis, int backDisposition) { } 1337 @Override 1338 public void setHardKeyboardStatus(boolean available, boolean enabled) { } 1339 1340 private class Launcher implements View.OnClickListener { 1341 private PendingIntent mIntent; 1342 private String mPkg; 1343 private String mTag; 1344 private int mId; 1345 1346 Launcher(PendingIntent intent, String pkg, String tag, int id) { 1347 mIntent = intent; 1348 mPkg = pkg; 1349 mTag = tag; 1350 mId = id; 1351 } 1352 1353 public void onClick(View v) { 1354 try { 1355 // The intent we are sending is for the application, which 1356 // won't have permission to immediately start an activity after 1357 // the user switches to home. We know it is safe to do at this 1358 // point, so make sure new activity switches are now allowed. 1359 ActivityManagerNative.getDefault().resumeAppSwitches(); 1360 } catch (RemoteException e) { 1361 } 1362 1363 if (mIntent != null) { 1364 int[] pos = new int[2]; 1365 v.getLocationOnScreen(pos); 1366 Intent overlay = new Intent(); 1367 overlay.setSourceBounds( 1368 new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight())); 1369 try { 1370 mIntent.send(mContext, 0, overlay); 1371 } catch (PendingIntent.CanceledException e) { 1372 // the stack trace isn't very helpful here. Just log the exception message. 1373 Slog.w(TAG, "Sending contentIntent failed: " + e); 1374 } 1375 } 1376 1377 try { 1378 mBarService.onNotificationClick(mPkg, mTag, mId); 1379 } catch (RemoteException ex) { 1380 // system process is dead if we're here. 1381 } 1382 1383 // close the shade if it was open 1384 animateCollapse(); 1385 1386 // If this click was on the intruder alert, hide that instead 1387 mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER); 1388 } 1389 } 1390 1391 private void tick(StatusBarNotification n) { 1392 // Show the ticker if one is requested. Also don't do this 1393 // until status bar window is attached to the window manager, 1394 // because... well, what's the point otherwise? And trying to 1395 // run a ticker without being attached will crash! 1396 if (n.notification.tickerText != null && mStatusBarView.getWindowToken() != null) { 1397 if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS 1398 | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) { 1399 mTicker.addEntry(n); 1400 } 1401 } 1402 } 1403 1404 /** 1405 * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService 1406 * about the failure. 1407 * 1408 * WARNING: this will call back into us. Don't hold any locks. 1409 */ 1410 void handleNotificationError(IBinder key, StatusBarNotification n, String message) { 1411 removeNotification(key); 1412 try { 1413 mBarService.onNotificationError(n.pkg, n.tag, n.id, n.uid, n.initialPid, message); 1414 } catch (RemoteException ex) { 1415 // The end is nigh. 1416 } 1417 } 1418 1419 private class MyTicker extends Ticker { 1420 MyTicker(Context context, View sb) { 1421 super(context, sb); 1422 } 1423 1424 @Override 1425 public void tickerStarting() { 1426 mTicking = true; 1427 mIcons.setVisibility(View.GONE); 1428 mTickerView.setVisibility(View.VISIBLE); 1429 mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null)); 1430 mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null)); 1431 if (mExpandedVisible) { 1432 setDateViewVisibility(false, com.android.internal.R.anim.push_up_out); 1433 } 1434 } 1435 1436 @Override 1437 public void tickerDone() { 1438 mIcons.setVisibility(View.VISIBLE); 1439 mTickerView.setVisibility(View.GONE); 1440 mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null)); 1441 mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out, 1442 mTickingDoneListener)); 1443 if (mExpandedVisible) { 1444 setDateViewVisibility(true, com.android.internal.R.anim.push_down_in); 1445 } 1446 } 1447 1448 public void tickerHalting() { 1449 mIcons.setVisibility(View.VISIBLE); 1450 mTickerView.setVisibility(View.GONE); 1451 mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null)); 1452 mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.fade_out, 1453 mTickingDoneListener)); 1454 if (mExpandedVisible) { 1455 setDateViewVisibility(true, com.android.internal.R.anim.fade_in); 1456 } 1457 } 1458 } 1459 1460 Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {; 1461 public void onAnimationEnd(Animation animation) { 1462 mTicking = false; 1463 } 1464 public void onAnimationRepeat(Animation animation) { 1465 } 1466 public void onAnimationStart(Animation animation) { 1467 } 1468 }; 1469 1470 private Animation loadAnim(int id, Animation.AnimationListener listener) { 1471 Animation anim = AnimationUtils.loadAnimation(mContext, id); 1472 if (listener != null) { 1473 anim.setAnimationListener(listener); 1474 } 1475 return anim; 1476 } 1477 1478 public String viewInfo(View v) { 1479 return "(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom() 1480 + " " + v.getWidth() + "x" + v.getHeight() + ")"; 1481 } 1482 1483 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1484 synchronized (mQueueLock) { 1485 pw.println("Current Status Bar state:"); 1486 pw.println(" mExpanded=" + mExpanded 1487 + ", mExpandedVisible=" + mExpandedVisible); 1488 pw.println(" mTicking=" + mTicking); 1489 pw.println(" mTracking=" + mTracking); 1490 pw.println(" mAnimating=" + mAnimating 1491 + ", mAnimY=" + mAnimY + ", mAnimVel=" + mAnimVel 1492 + ", mAnimAccel=" + mAnimAccel); 1493 pw.println(" mCurAnimationTime=" + mCurAnimationTime 1494 + " mAnimLastTime=" + mAnimLastTime); 1495 pw.println(" mDisplayHeight=" + mDisplayHeight 1496 + " mAnimatingReveal=" + mAnimatingReveal 1497 + " mViewDelta=" + mViewDelta); 1498 pw.println(" mDisplayHeight=" + mDisplayHeight); 1499 pw.println(" mExpandedParams: " + mExpandedParams); 1500 pw.println(" mExpandedView: " + viewInfo(mExpandedView)); 1501 pw.println(" mExpandedDialog: " + mExpandedDialog); 1502 pw.println(" mTrackingParams: " + mTrackingParams); 1503 pw.println(" mTrackingView: " + viewInfo(mTrackingView)); 1504 pw.println(" mOngoingTitle: " + viewInfo(mOngoingTitle)); 1505 pw.println(" mOngoingItems: " + viewInfo(mOngoingItems)); 1506 pw.println(" mLatestTitle: " + viewInfo(mLatestTitle)); 1507 pw.println(" mLatestItems: " + viewInfo(mLatestItems)); 1508 pw.println(" mNoNotificationsTitle: " + viewInfo(mNoNotificationsTitle)); 1509 pw.println(" mCloseView: " + viewInfo(mCloseView)); 1510 pw.println(" mTickerView: " + viewInfo(mTickerView)); 1511 pw.println(" mScrollView: " + viewInfo(mScrollView) 1512 + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY()); 1513 pw.println("mNotificationLinearLayout: " + viewInfo(mNotificationLinearLayout)); 1514 } 1515 /* 1516 synchronized (mNotificationData) { 1517 int N = mNotificationData.ongoingCount(); 1518 pw.println(" ongoingCount.size=" + N); 1519 for (int i=0; i<N; i++) { 1520 StatusBarNotification n = mNotificationData.getOngoing(i); 1521 pw.println(" [" + i + "] key=" + n.key + " view=" + n.view); 1522 pw.println(" data=" + n.data); 1523 } 1524 N = mNotificationData.latestCount(); 1525 pw.println(" ongoingCount.size=" + N); 1526 for (int i=0; i<N; i++) { 1527 StatusBarNotification n = mNotificationData.getLatest(i); 1528 pw.println(" [" + i + "] key=" + n.key + " view=" + n.view); 1529 pw.println(" data=" + n.data); 1530 } 1531 } 1532 */ 1533 1534 if (false) { 1535 pw.println("see the logcat for a dump of the views we have created."); 1536 // must happen on ui thread 1537 mHandler.post(new Runnable() { 1538 public void run() { 1539 mStatusBarView.getLocationOnScreen(mAbsPos); 1540 Slog.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1] 1541 + ") " + mStatusBarView.getWidth() + "x" 1542 + mStatusBarView.getHeight()); 1543 mStatusBarView.debug(); 1544 1545 mExpandedView.getLocationOnScreen(mAbsPos); 1546 Slog.d(TAG, "mExpandedView: ----- (" + mAbsPos[0] + "," + mAbsPos[1] 1547 + ") " + mExpandedView.getWidth() + "x" 1548 + mExpandedView.getHeight()); 1549 mExpandedView.debug(); 1550 1551 mTrackingView.getLocationOnScreen(mAbsPos); 1552 Slog.d(TAG, "mTrackingView: ----- (" + mAbsPos[0] + "," + mAbsPos[1] 1553 + ") " + mTrackingView.getWidth() + "x" 1554 + mTrackingView.getHeight()); 1555 mTrackingView.debug(); 1556 } 1557 }); 1558 } 1559 } 1560 1561 void onBarViewAttached() { 1562 WindowManager.LayoutParams lp; 1563 int pixelFormat; 1564 Drawable bg; 1565 1566 /// ---------- Tracking View -------------- 1567 pixelFormat = PixelFormat.RGBX_8888; 1568 bg = mTrackingView.getBackground(); 1569 if (bg != null) { 1570 pixelFormat = bg.getOpacity(); 1571 } 1572 1573 lp = new WindowManager.LayoutParams( 1574 ViewGroup.LayoutParams.MATCH_PARENT, 1575 ViewGroup.LayoutParams.MATCH_PARENT, 1576 WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL, 1577 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 1578 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 1579 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, 1580 pixelFormat); 1581// lp.token = mStatusBarView.getWindowToken(); 1582 lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; 1583 lp.setTitle("TrackingView"); 1584 lp.y = mTrackingPosition; 1585 mTrackingParams = lp; 1586 1587 WindowManagerImpl.getDefault().addView(mTrackingView, lp); 1588 } 1589 1590 void onTrackingViewAttached() { 1591 WindowManager.LayoutParams lp; 1592 int pixelFormat; 1593 Drawable bg; 1594 1595 /// ---------- Expanded View -------------- 1596 pixelFormat = PixelFormat.TRANSLUCENT; 1597 1598 final int disph = mDisplay.getHeight(); 1599 lp = mExpandedDialog.getWindow().getAttributes(); 1600 lp.width = ViewGroup.LayoutParams.MATCH_PARENT; 1601 lp.height = getExpandedHeight(); 1602 lp.x = 0; 1603 mTrackingPosition = lp.y = -disph; // sufficiently large negative 1604 lp.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; 1605 lp.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 1606 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 1607 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 1608 | WindowManager.LayoutParams.FLAG_DITHER 1609 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 1610 lp.format = pixelFormat; 1611 lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; 1612 lp.setTitle("StatusBarExpanded"); 1613 mExpandedDialog.getWindow().setAttributes(lp); 1614 mExpandedDialog.getWindow().setFormat(pixelFormat); 1615 mExpandedParams = lp; 1616 1617 mExpandedDialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE); 1618 mExpandedDialog.setContentView(mExpandedView, 1619 new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1620 ViewGroup.LayoutParams.MATCH_PARENT)); 1621 mExpandedDialog.getWindow().setBackgroundDrawable(null); 1622 mExpandedDialog.show(); 1623 FrameLayout hack = (FrameLayout)mExpandedView.getParent(); 1624 } 1625 1626 void setDateViewVisibility(boolean visible, int anim) { 1627 mDateView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); 1628 mDateView.startAnimation(loadAnim(anim, null)); 1629 } 1630 1631 void setNotificationIconVisibility(boolean visible, int anim) { 1632 int old = mNotificationIcons.getVisibility(); 1633 int v = visible ? View.VISIBLE : View.INVISIBLE; 1634 if (old != v) { 1635 mNotificationIcons.setVisibility(v); 1636 mNotificationIcons.startAnimation(loadAnim(anim, null)); 1637 } 1638 } 1639 1640 void updateExpandedViewPos(int expandedPosition) { 1641 if (SPEW) { 1642 Slog.d(TAG, "updateExpandedViewPos before expandedPosition=" + expandedPosition 1643 + " mTrackingParams.y=" + mTrackingParams.y 1644 + " mTrackingPosition=" + mTrackingPosition); 1645 } 1646 1647 int h = mStatusBarView.getHeight(); 1648 int disph = mDisplay.getHeight(); 1649 1650 // If the expanded view is not visible, make sure they're still off screen. 1651 // Maybe the view was resized. 1652 if (!mExpandedVisible) { 1653 if (mTrackingView != null) { 1654 mTrackingPosition = -disph; 1655 if (mTrackingParams != null) { 1656 mTrackingParams.y = mTrackingPosition; 1657 WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams); 1658 } 1659 } 1660 if (mExpandedParams != null) { 1661 mExpandedParams.y = -disph; 1662 mExpandedDialog.getWindow().setAttributes(mExpandedParams); 1663 } 1664 return; 1665 } 1666 1667 // tracking view... 1668 int pos; 1669 if (expandedPosition == EXPANDED_FULL_OPEN) { 1670 pos = h; 1671 } 1672 else if (expandedPosition == EXPANDED_LEAVE_ALONE) { 1673 pos = mTrackingPosition; 1674 } 1675 else { 1676 if (expandedPosition <= disph) { 1677 pos = expandedPosition; 1678 } else { 1679 pos = disph; 1680 } 1681 pos -= disph-h; 1682 } 1683 mTrackingPosition = mTrackingParams.y = pos; 1684 mTrackingParams.height = disph-h; 1685 WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams); 1686 1687 if (mExpandedParams != null) { 1688 mCloseView.getLocationInWindow(mPositionTmp); 1689 final int closePos = mPositionTmp[1]; 1690 1691 mExpandedContents.getLocationInWindow(mPositionTmp); 1692 final int contentsBottom = mPositionTmp[1] + mExpandedContents.getHeight(); 1693 1694 mExpandedParams.y = pos + mTrackingView.getHeight() 1695 - (mTrackingParams.height-closePos) - contentsBottom; 1696 int max = h; 1697 if (mExpandedParams.y > max) { 1698 mExpandedParams.y = max; 1699 } 1700 int min = mTrackingPosition; 1701 if (mExpandedParams.y < min) { 1702 mExpandedParams.y = min; 1703 } 1704 1705 boolean visible = (mTrackingPosition + mTrackingView.getHeight()) > h; 1706 if (!visible) { 1707 // if the contents aren't visible, move the expanded view way off screen 1708 // because the window itself extends below the content view. 1709 mExpandedParams.y = -disph; 1710 } 1711 mExpandedDialog.getWindow().setAttributes(mExpandedParams); 1712 1713 // As long as this isn't just a repositioning that's not supposed to affect 1714 // the user's perception of what's showing, call to say that the visibility 1715 // has changed. (Otherwise, someone else will call to do that). 1716 if (expandedPosition != EXPANDED_LEAVE_ALONE) { 1717 if (SPEW) Slog.d(TAG, "updateExpandedViewPos visibilityChanged(" + visible + ")"); 1718 visibilityChanged(visible); 1719 } 1720 } 1721 1722 if (SPEW) { 1723 Slog.d(TAG, "updateExpandedViewPos after expandedPosition=" + expandedPosition 1724 + " mTrackingParams.y=" + mTrackingParams.y 1725 + " mTrackingPosition=" + mTrackingPosition 1726 + " mExpandedParams.y=" + mExpandedParams.y 1727 + " mExpandedParams.height=" + mExpandedParams.height); 1728 } 1729 } 1730 1731 int getExpandedHeight() { 1732 return mDisplay.getHeight() - mStatusBarView.getHeight() - mCloseView.getHeight(); 1733 } 1734 1735 void updateExpandedHeight() { 1736 if (mExpandedView != null) { 1737 mExpandedParams.height = getExpandedHeight(); 1738 mExpandedDialog.getWindow().setAttributes(mExpandedParams); 1739 } 1740 } 1741 1742 // The user is not allowed to get stuck without navigation UI. Upon the slightest user 1743 // interaction we bring the navigation back. 1744 public void userActivity() { 1745 if (mNavigationBarView != null) { 1746 mNavigationBarView.setHidden(false); 1747 } 1748 } 1749 1750 public void toggleRecentApps() { 1751 int msg = (mRecentsPanel.getVisibility() == View.GONE) 1752 ? MSG_OPEN_RECENTS_PANEL : MSG_CLOSE_RECENTS_PANEL; 1753 mHandler.removeMessages(msg); 1754 mHandler.sendEmptyMessage(msg); 1755 } 1756 1757 /** 1758 * The LEDs are turned o)ff when the notification panel is shown, even just a little bit. 1759 * This was added last-minute and is inconsistent with the way the rest of the notifications 1760 * are handled, because the notification isn't really cancelled. The lights are just 1761 * turned off. If any other notifications happen, the lights will turn back on. Steve says 1762 * this is what he wants. (see bug 1131461) 1763 */ 1764 void visibilityChanged(boolean visible) { 1765 if (mPanelSlightlyVisible != visible) { 1766 mPanelSlightlyVisible = visible; 1767 try { 1768 mBarService.onPanelRevealed(); 1769 } catch (RemoteException ex) { 1770 // Won't fail unless the world has ended. 1771 } 1772 } 1773 } 1774 1775 void performDisableActions(int net) { 1776 int old = mDisabled; 1777 int diff = net ^ old; 1778 mDisabled = net; 1779 1780 // act accordingly 1781 if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) { 1782 if ((net & StatusBarManager.DISABLE_EXPAND) != 0) { 1783 Slog.d(TAG, "DISABLE_EXPAND: yes"); 1784 animateCollapse(); 1785 } 1786 } 1787 if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { 1788 if ((net & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { 1789 Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes"); 1790 if (mTicking) { 1791 mNotificationIcons.setVisibility(View.INVISIBLE); 1792 mTicker.halt(); 1793 } else { 1794 setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out); 1795 } 1796 } else { 1797 Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no"); 1798 if (!mExpandedVisible) { 1799 setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in); 1800 } 1801 } 1802 } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) { 1803 if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) { 1804 mTicker.halt(); 1805 } 1806 } 1807 } 1808 1809 private View.OnClickListener mClearButtonListener = new View.OnClickListener() { 1810 public void onClick(View v) { 1811 try { 1812 mBarService.onClearAllNotifications(); 1813 } catch (RemoteException ex) { 1814 // system process is dead if we're here. 1815 } 1816 animateCollapse(); 1817 } 1818 }; 1819 1820 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 1821 public void onReceive(Context context, Intent intent) { 1822 String action = intent.getAction(); 1823 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action) 1824 || Intent.ACTION_SCREEN_OFF.equals(action)) { 1825 boolean excludeRecents = false; 1826 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { 1827 String reason = intent.getStringExtra("reason"); 1828 if (reason != null) { 1829 excludeRecents = reason.equals("recentapps"); 1830 } 1831 } 1832 animateCollapse(excludeRecents); 1833 } 1834 else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) { 1835 repositionNavigationBar(); 1836 updateResources(); 1837 } 1838 } 1839 }; 1840 1841 private void setIntruderAlertVisibility(boolean vis) { 1842 mIntruderAlertView.setVisibility(vis ? View.VISIBLE : View.GONE); 1843 } 1844 1845 /** 1846 * Reload some of our resources when the configuration changes. 1847 * 1848 * We don't reload everything when the configuration changes -- we probably 1849 * should, but getting that smooth is tough. Someday we'll fix that. In the 1850 * meantime, just update the things that we know change. 1851 */ 1852 void updateResources() { 1853 final Context context = mContext; 1854 final Resources res = context.getResources(); 1855 1856 mClearButton.setText(context.getText(R.string.status_bar_clear_all_button)); 1857 mOngoingTitle.setText(context.getText(R.string.status_bar_ongoing_events_title)); 1858 mLatestTitle.setText(context.getText(R.string.status_bar_latest_events_title)); 1859 mNoNotificationsTitle.setText(context.getText(R.string.status_bar_no_notifications_title)); 1860 1861 mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore); 1862 1863 if (false) Slog.v(TAG, "updateResources"); 1864 } 1865 1866 // 1867 // tracing 1868 // 1869 1870 void postStartTracing() { 1871 mHandler.postDelayed(mStartTracing, 3000); 1872 } 1873 1874 void vibrate() { 1875 android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService( 1876 Context.VIBRATOR_SERVICE); 1877 vib.vibrate(250); 1878 } 1879 1880 Runnable mStartTracing = new Runnable() { 1881 public void run() { 1882 vibrate(); 1883 SystemClock.sleep(250); 1884 Slog.d(TAG, "startTracing"); 1885 android.os.Debug.startMethodTracing("/data/statusbar-traces/trace"); 1886 mHandler.postDelayed(mStopTracing, 10000); 1887 } 1888 }; 1889 1890 Runnable mStopTracing = new Runnable() { 1891 public void run() { 1892 android.os.Debug.stopMethodTracing(); 1893 Slog.d(TAG, "stopTracing"); 1894 vibrate(); 1895 } 1896 }; 1897 1898 public class TouchOutsideListener implements View.OnTouchListener { 1899 private int mMsg; 1900 private RecentsPanelView mPanel; 1901 1902 public TouchOutsideListener(int msg, RecentsPanelView panel) { 1903 mMsg = msg; 1904 mPanel = panel; 1905 } 1906 1907 public boolean onTouch(View v, MotionEvent ev) { 1908 final int action = ev.getAction(); 1909 if (action == MotionEvent.ACTION_OUTSIDE 1910 || (action == MotionEvent.ACTION_DOWN 1911 && !mPanel.isInContentArea((int)ev.getX(), (int)ev.getY()))) { 1912 mHandler.removeMessages(mMsg); 1913 mHandler.sendEmptyMessage(mMsg); 1914 return true; 1915 } 1916 return false; 1917 } 1918 } 1919} 1920 1921