TabletStatusBar.java revision f7a1956b06faeb6025fdea331f81d66edda58b3c
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.LayoutTransition; 20import android.animation.ObjectAnimator; 21import android.app.ActivityManagerNative; 22import android.app.Notification; 23import android.app.PendingIntent; 24import android.app.StatusBarManager; 25import android.content.BroadcastReceiver; 26import android.content.Context; 27import android.content.Intent; 28import android.content.IntentFilter; 29import android.content.SharedPreferences; 30import android.content.res.Configuration; 31import android.content.res.Resources; 32import android.graphics.PixelFormat; 33import android.graphics.Point; 34import android.graphics.drawable.Drawable; 35import android.graphics.drawable.LayerDrawable; 36import android.inputmethodservice.InputMethodService; 37import android.os.IBinder; 38import android.os.Message; 39import android.os.RemoteException; 40import android.os.ServiceManager; 41import android.text.TextUtils; 42import android.util.Slog; 43import android.view.Display; 44import android.view.Gravity; 45import android.view.IWindowManager; 46import android.view.KeyEvent; 47import android.view.MotionEvent; 48import android.view.SoundEffectConstants; 49import android.view.VelocityTracker; 50import android.view.View; 51import android.view.ViewConfiguration; 52import android.view.ViewGroup; 53import android.view.ViewGroup.LayoutParams; 54import android.view.WindowManager; 55import android.view.WindowManagerImpl; 56import android.view.accessibility.AccessibilityEvent; 57import android.widget.ImageView; 58import android.widget.LinearLayout; 59import android.widget.RemoteViews; 60import android.widget.ScrollView; 61import android.widget.TextView; 62 63import com.android.internal.statusbar.StatusBarIcon; 64import com.android.internal.statusbar.StatusBarNotification; 65import com.android.systemui.R; 66import com.android.systemui.recent.RecentTasksLoader; 67import com.android.systemui.recent.RecentsPanelView; 68import com.android.systemui.statusbar.BaseStatusBar; 69import com.android.systemui.statusbar.NotificationData; 70import com.android.systemui.statusbar.SignalClusterView; 71import com.android.systemui.statusbar.StatusBarIconView; 72import com.android.systemui.statusbar.policy.BatteryController; 73import com.android.systemui.statusbar.policy.BluetoothController; 74import com.android.systemui.statusbar.policy.CompatModeButton; 75import com.android.systemui.statusbar.policy.LocationController; 76import com.android.systemui.statusbar.policy.NetworkController; 77import com.android.systemui.statusbar.policy.NotificationRowLayout; 78import com.android.systemui.statusbar.policy.Prefs; 79 80import java.io.FileDescriptor; 81import java.io.PrintWriter; 82import java.util.ArrayList; 83 84public class TabletStatusBar extends BaseStatusBar implements 85 InputMethodsPanel.OnHardKeyboardEnabledChangeListener, 86 RecentsPanelView.OnRecentsPanelVisibilityChangedListener { 87 public static final boolean DEBUG = false; 88 public static final boolean DEBUG_COMPAT_HELP = false; 89 public static final String TAG = "TabletStatusBar"; 90 91 92 public static final int MSG_OPEN_NOTIFICATION_PANEL = 1000; 93 public static final int MSG_CLOSE_NOTIFICATION_PANEL = 1001; 94 public static final int MSG_OPEN_NOTIFICATION_PEEK = 1002; 95 public static final int MSG_CLOSE_NOTIFICATION_PEEK = 1003; 96 // 1020-1029 reserved for BaseStatusBar 97 public static final int MSG_SHOW_CHROME = 1030; 98 public static final int MSG_HIDE_CHROME = 1031; 99 public static final int MSG_OPEN_INPUT_METHODS_PANEL = 1040; 100 public static final int MSG_CLOSE_INPUT_METHODS_PANEL = 1041; 101 public static final int MSG_OPEN_COMPAT_MODE_PANEL = 1050; 102 public static final int MSG_CLOSE_COMPAT_MODE_PANEL = 1051; 103 public static final int MSG_STOP_TICKER = 2000; 104 105 // Fitts' Law assistance for LatinIME; see policy.EventHole 106 private static final boolean FAKE_SPACE_BAR = true; 107 108 // Notification "peeking" (flyover preview of individual notifications) 109 final static boolean NOTIFICATION_PEEK_ENABLED = false; 110 final static int NOTIFICATION_PEEK_HOLD_THRESH = 200; // ms 111 final static int NOTIFICATION_PEEK_FADE_DELAY = 3000; // ms 112 113 // The height of the bar, as definied by the build. It may be taller if we're plugged 114 // into hdmi. 115 int mNaturalBarHeight = -1; 116 int mIconSize = -1; 117 int mIconHPadding = -1; 118 int mNavIconWidth = -1; 119 int mMenuNavIconWidth = -1; 120 private int mMaxNotificationIcons = 5; 121 122 IWindowManager mWindowManager; 123 124 // tracking all current notifications 125 private NotificationData mNotificationData = new NotificationData(); 126 127 TabletStatusBarView mStatusBarView; 128 View mNotificationArea; 129 View mNotificationTrigger; 130 NotificationIconArea mNotificationIconArea; 131 ViewGroup mNavigationArea; 132 133 boolean mNotificationDNDMode; 134 NotificationData.Entry mNotificationDNDDummyEntry; 135 136 ImageView mBackButton; 137 View mHomeButton; 138 View mMenuButton; 139 View mRecentButton; 140 private boolean mAltBackButtonEnabledForIme; 141 142 ViewGroup mFeedbackIconArea; // notification icons, IME icon, compat icon 143 InputMethodButton mInputMethodSwitchButton; 144 CompatModeButton mCompatModeButton; 145 146 NotificationPanel mNotificationPanel; 147 WindowManager.LayoutParams mNotificationPanelParams; 148 NotificationPeekPanel mNotificationPeekWindow; 149 ViewGroup mNotificationPeekRow; 150 int mNotificationPeekIndex; 151 IBinder mNotificationPeekKey; 152 LayoutTransition mNotificationPeekScrubLeft, mNotificationPeekScrubRight; 153 154 int mNotificationPeekTapDuration; 155 int mNotificationFlingVelocity; 156 157 NotificationRowLayout mPile; 158 159 BatteryController mBatteryController; 160 BluetoothController mBluetoothController; 161 LocationController mLocationController; 162 NetworkController mNetworkController; 163 164 ViewGroup mBarContents; 165 166 // hide system chrome ("lights out") support 167 View mShadow; 168 169 NotificationIconArea.IconLayout mIconLayout; 170 171 TabletTicker mTicker; 172 173 View mFakeSpaceBar; 174 KeyEvent mSpaceBarKeyEvent = null; 175 176 View mCompatibilityHelpDialog = null; 177 178 // for disabling the status bar 179 int mDisabled = 0; 180 181 private InputMethodsPanel mInputMethodsPanel; 182 private CompatModePanel mCompatModePanel; 183 184 private int mSystemUiVisibility = 0; 185 186 private int mNavigationIconHints = 0; 187 188 public Context getContext() { return mContext; } 189 190 @Override 191 protected void createAndAddWindows() { 192 addStatusBarWindow(); 193 addPanelWindows(); 194 } 195 196 private void addStatusBarWindow() { 197 final View sb = makeStatusBarView(); 198 199 final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 200 ViewGroup.LayoutParams.MATCH_PARENT, 201 ViewGroup.LayoutParams.MATCH_PARENT, 202 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, 203 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 204 | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING 205 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, 206 // We use a pixel format of RGB565 for the status bar to save memory bandwidth and 207 // to ensure that the layer can be handled by HWComposer. On some devices the 208 // HWComposer is unable to handle SW-rendered RGBX_8888 layers. 209 PixelFormat.RGB_565); 210 211 // We explicitly leave FLAG_HARDWARE_ACCELERATED out of the flags. The status bar occupies 212 // very little screen real-estate and is updated fairly frequently. By using CPU rendering 213 // for the status bar, we prevent the GPU from having to wake up just to do these small 214 // updates, which should help keep power consumption down. 215 216 lp.gravity = getStatusBarGravity(); 217 lp.setTitle("SystemBar"); 218 lp.packageName = mContext.getPackageName(); 219 WindowManagerImpl.getDefault().addView(sb, lp); 220 } 221 222 protected void addPanelWindows() { 223 final Context context = mContext; 224 final Resources res = mContext.getResources(); 225 226 // Notification Panel 227 mNotificationPanel = (NotificationPanel)View.inflate(context, 228 R.layout.status_bar_notification_panel, null); 229 mNotificationPanel.setBar(this); 230 mNotificationPanel.show(false, false); 231 mNotificationPanel.setOnTouchListener( 232 new TouchOutsideListener(MSG_CLOSE_NOTIFICATION_PANEL, mNotificationPanel)); 233 234 // the battery icon 235 mBatteryController.addIconView((ImageView)mNotificationPanel.findViewById(R.id.battery)); 236 mBatteryController.addLabelView( 237 (TextView)mNotificationPanel.findViewById(R.id.battery_text)); 238 239 // Bt 240 mBluetoothController.addIconView( 241 (ImageView)mNotificationPanel.findViewById(R.id.bluetooth)); 242 243 // network icons: either a combo icon that switches between mobile and data, or distinct 244 // mobile and data icons 245 final ImageView mobileRSSI = 246 (ImageView)mNotificationPanel.findViewById(R.id.mobile_signal); 247 if (mobileRSSI != null) { 248 mNetworkController.addPhoneSignalIconView(mobileRSSI); 249 } 250 final ImageView wifiRSSI = 251 (ImageView)mNotificationPanel.findViewById(R.id.wifi_signal); 252 if (wifiRSSI != null) { 253 mNetworkController.addWifiIconView(wifiRSSI); 254 } 255 mNetworkController.addWifiLabelView( 256 (TextView)mNotificationPanel.findViewById(R.id.wifi_text)); 257 258 mNetworkController.addDataTypeIconView( 259 (ImageView)mNotificationPanel.findViewById(R.id.mobile_type)); 260 mNetworkController.addMobileLabelView( 261 (TextView)mNotificationPanel.findViewById(R.id.mobile_text)); 262 mNetworkController.addCombinedLabelView( 263 (TextView)mBarContents.findViewById(R.id.network_text)); 264 265 mStatusBarView.setIgnoreChildren(0, mNotificationTrigger, mNotificationPanel); 266 267 WindowManager.LayoutParams lp = mNotificationPanelParams = new WindowManager.LayoutParams( 268 res.getDimensionPixelSize(R.dimen.notification_panel_width), 269 getNotificationPanelHeight(), 270 WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, 271 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 272 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 273 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM 274 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH 275 | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, 276 PixelFormat.TRANSLUCENT); 277 lp.gravity = Gravity.BOTTOM | Gravity.RIGHT; 278 lp.setTitle("NotificationPanel"); 279 lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED 280 | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; 281 lp.windowAnimations = com.android.internal.R.style.Animation; // == no animation 282// lp.windowAnimations = com.android.internal.R.style.Animation_ZoomButtons; // simple fade 283 284 WindowManagerImpl.getDefault().addView(mNotificationPanel, lp); 285 286 // Notification preview window 287 if (NOTIFICATION_PEEK_ENABLED) { 288 mNotificationPeekWindow = (NotificationPeekPanel) View.inflate(context, 289 R.layout.status_bar_notification_peek, null); 290 mNotificationPeekWindow.setBar(this); 291 292 mNotificationPeekRow = (ViewGroup) mNotificationPeekWindow.findViewById(R.id.content); 293 mNotificationPeekWindow.setVisibility(View.GONE); 294 mNotificationPeekWindow.setOnTouchListener( 295 new TouchOutsideListener(MSG_CLOSE_NOTIFICATION_PEEK, mNotificationPeekWindow)); 296 mNotificationPeekScrubRight = new LayoutTransition(); 297 mNotificationPeekScrubRight.setAnimator(LayoutTransition.APPEARING, 298 ObjectAnimator.ofInt(null, "left", -512, 0)); 299 mNotificationPeekScrubRight.setAnimator(LayoutTransition.DISAPPEARING, 300 ObjectAnimator.ofInt(null, "left", -512, 0)); 301 mNotificationPeekScrubRight.setDuration(500); 302 303 mNotificationPeekScrubLeft = new LayoutTransition(); 304 mNotificationPeekScrubLeft.setAnimator(LayoutTransition.APPEARING, 305 ObjectAnimator.ofInt(null, "left", 512, 0)); 306 mNotificationPeekScrubLeft.setAnimator(LayoutTransition.DISAPPEARING, 307 ObjectAnimator.ofInt(null, "left", 512, 0)); 308 mNotificationPeekScrubLeft.setDuration(500); 309 310 // XXX: setIgnoreChildren? 311 lp = new WindowManager.LayoutParams( 312 512, // ViewGroup.LayoutParams.WRAP_CONTENT, 313 ViewGroup.LayoutParams.WRAP_CONTENT, 314 WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, 315 WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 316 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM 317 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, 318 PixelFormat.TRANSLUCENT); 319 lp.gravity = Gravity.BOTTOM | Gravity.RIGHT; 320 lp.y = res.getDimensionPixelOffset(R.dimen.peek_window_y_offset); 321 lp.setTitle("NotificationPeekWindow"); 322 lp.windowAnimations = com.android.internal.R.style.Animation_Toast; 323 324 WindowManagerImpl.getDefault().addView(mNotificationPeekWindow, lp); 325 } 326 327 // Recents Panel 328 mRecentTasksLoader = new RecentTasksLoader(context); 329 updateRecentsPanel(); 330 331 // Input methods Panel 332 mInputMethodsPanel = (InputMethodsPanel) View.inflate(context, 333 R.layout.status_bar_input_methods_panel, null); 334 mInputMethodsPanel.setHardKeyboardEnabledChangeListener(this); 335 mInputMethodsPanel.setOnTouchListener(new TouchOutsideListener( 336 MSG_CLOSE_INPUT_METHODS_PANEL, mInputMethodsPanel)); 337 mInputMethodsPanel.setImeSwitchButton(mInputMethodSwitchButton); 338 mStatusBarView.setIgnoreChildren(2, mInputMethodSwitchButton, mInputMethodsPanel); 339 lp = new WindowManager.LayoutParams( 340 ViewGroup.LayoutParams.WRAP_CONTENT, 341 ViewGroup.LayoutParams.WRAP_CONTENT, 342 WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, 343 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 344 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM 345 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH 346 | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, 347 PixelFormat.TRANSLUCENT); 348 lp.gravity = Gravity.BOTTOM | Gravity.RIGHT; 349 lp.setTitle("InputMethodsPanel"); 350 lp.windowAnimations = R.style.Animation_RecentPanel; 351 352 WindowManagerImpl.getDefault().addView(mInputMethodsPanel, lp); 353 354 // Compatibility mode selector panel 355 mCompatModePanel = (CompatModePanel) View.inflate(context, 356 R.layout.status_bar_compat_mode_panel, null); 357 mCompatModePanel.setOnTouchListener(new TouchOutsideListener( 358 MSG_CLOSE_COMPAT_MODE_PANEL, mCompatModePanel)); 359 mCompatModePanel.setTrigger(mCompatModeButton); 360 mCompatModePanel.setVisibility(View.GONE); 361 mStatusBarView.setIgnoreChildren(3, mCompatModeButton, mCompatModePanel); 362 lp = new WindowManager.LayoutParams( 363 250, 364 ViewGroup.LayoutParams.WRAP_CONTENT, 365 WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, 366 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 367 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM 368 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH 369 | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, 370 PixelFormat.TRANSLUCENT); 371 lp.gravity = Gravity.BOTTOM | Gravity.RIGHT; 372 lp.setTitle("CompatModePanel"); 373 lp.windowAnimations = android.R.style.Animation_Dialog; 374 375 WindowManagerImpl.getDefault().addView(mCompatModePanel, lp); 376 377 mRecentButton.setOnTouchListener(mRecentsPanel); 378 379 mPile = (NotificationRowLayout)mNotificationPanel.findViewById(R.id.content); 380 mPile.removeAllViews(); 381 mPile.setLongPressListener(getNotificationLongClicker()); 382 383 ScrollView scroller = (ScrollView)mPile.getParent(); 384 scroller.setFillViewport(true); 385 } 386 387 private int getNotificationPanelHeight() { 388 final Resources res = mContext.getResources(); 389 final Display d = WindowManagerImpl.getDefault().getDefaultDisplay(); 390 final Point size = new Point(); 391 d.getRealSize(size); 392 return Math.max(res.getDimensionPixelSize(R.dimen.notification_panel_min_height), size.y); 393 } 394 395 @Override 396 public void start() { 397 super.start(); // will add the main bar view 398 } 399 400 @Override 401 protected void onConfigurationChanged(Configuration newConfig) { 402 loadDimens(); 403 mNotificationPanelParams.height = getNotificationPanelHeight(); 404 WindowManagerImpl.getDefault().updateViewLayout(mNotificationPanel, 405 mNotificationPanelParams); 406 mRecentsPanel.updateValuesFromResources(); 407 } 408 409 protected void loadDimens() { 410 final Resources res = mContext.getResources(); 411 412 mNaturalBarHeight = res.getDimensionPixelSize( 413 com.android.internal.R.dimen.navigation_bar_height); 414 415 int newIconSize = res.getDimensionPixelSize( 416 com.android.internal.R.dimen.system_bar_icon_size); 417 int newIconHPadding = res.getDimensionPixelSize( 418 R.dimen.status_bar_icon_padding); 419 int newNavIconWidth = res.getDimensionPixelSize(R.dimen.navigation_key_width); 420 int newMenuNavIconWidth = res.getDimensionPixelSize(R.dimen.navigation_menu_key_width); 421 422 if (mNavigationArea != null && newNavIconWidth != mNavIconWidth) { 423 mNavIconWidth = newNavIconWidth; 424 425 LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( 426 mNavIconWidth, ViewGroup.LayoutParams.MATCH_PARENT); 427 mBackButton.setLayoutParams(lp); 428 mHomeButton.setLayoutParams(lp); 429 mRecentButton.setLayoutParams(lp); 430 } 431 432 if (mNavigationArea != null && newMenuNavIconWidth != mMenuNavIconWidth) { 433 mMenuNavIconWidth = newMenuNavIconWidth; 434 435 LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( 436 mMenuNavIconWidth, ViewGroup.LayoutParams.MATCH_PARENT); 437 mMenuButton.setLayoutParams(lp); 438 } 439 440 if (newIconHPadding != mIconHPadding || newIconSize != mIconSize) { 441// Slog.d(TAG, "size=" + newIconSize + " padding=" + newIconHPadding); 442 mIconHPadding = newIconHPadding; 443 mIconSize = newIconSize; 444 reloadAllNotificationIcons(); // reload the tray 445 } 446 447 final int numIcons = res.getInteger(R.integer.config_maxNotificationIcons); 448 if (numIcons != mMaxNotificationIcons) { 449 mMaxNotificationIcons = numIcons; 450 if (DEBUG) Slog.d(TAG, "max notification icons: " + mMaxNotificationIcons); 451 reloadAllNotificationIcons(); 452 } 453 } 454 455 public View getStatusBarView() { 456 return mStatusBarView; 457 } 458 459 protected View makeStatusBarView() { 460 final Context context = mContext; 461 462 mWindowManager = IWindowManager.Stub.asInterface( 463 ServiceManager.getService(Context.WINDOW_SERVICE)); 464 465 loadDimens(); 466 467 final TabletStatusBarView sb = (TabletStatusBarView)View.inflate( 468 context, R.layout.status_bar, null); 469 mStatusBarView = sb; 470 471 sb.setHandler(mHandler); 472 473 try { 474 // Sanity-check that someone hasn't set up the config wrong and asked for a navigation 475 // bar on a tablet that has only the system bar 476 if (mWindowManager.hasNavigationBar()) { 477 Slog.e(TAG, "Tablet device cannot show navigation bar and system bar"); 478 } 479 } catch (RemoteException ex) { 480 } 481 482 mBarContents = (ViewGroup) sb.findViewById(R.id.bar_contents); 483 484 // the whole right-hand side of the bar 485 mNotificationArea = sb.findViewById(R.id.notificationArea); 486 if (!NOTIFICATION_PEEK_ENABLED) { 487 mNotificationArea.setOnTouchListener(new NotificationTriggerTouchListener()); 488 } 489 490 // the button to open the notification area 491 mNotificationTrigger = sb.findViewById(R.id.notificationTrigger); 492 if (NOTIFICATION_PEEK_ENABLED) { 493 mNotificationTrigger.setOnTouchListener(new NotificationTriggerTouchListener()); 494 } 495 496 // the more notifications icon 497 mNotificationIconArea = (NotificationIconArea)sb.findViewById(R.id.notificationIcons); 498 499 // where the icons go 500 mIconLayout = (NotificationIconArea.IconLayout) sb.findViewById(R.id.icons); 501 if (NOTIFICATION_PEEK_ENABLED) { 502 mIconLayout.setOnTouchListener(new NotificationIconTouchListener()); 503 } 504 505 ViewConfiguration vc = ViewConfiguration.get(context); 506 mNotificationPeekTapDuration = vc.getTapTimeout(); 507 mNotificationFlingVelocity = 300; // px/s 508 509 mTicker = new TabletTicker(this); 510 511 // The icons 512 mLocationController = new LocationController(mContext); // will post a notification 513 514 mBatteryController = new BatteryController(mContext); 515 mBatteryController.addIconView((ImageView)sb.findViewById(R.id.battery)); 516 mBluetoothController = new BluetoothController(mContext); 517 mBluetoothController.addIconView((ImageView)sb.findViewById(R.id.bluetooth)); 518 519 mNetworkController = new NetworkController(mContext); 520 final SignalClusterView signalCluster = 521 (SignalClusterView)sb.findViewById(R.id.signal_cluster); 522 mNetworkController.addSignalCluster(signalCluster); 523 524 // The navigation buttons 525 mBackButton = (ImageView)sb.findViewById(R.id.back); 526 mNavigationArea = (ViewGroup) sb.findViewById(R.id.navigationArea); 527 mHomeButton = mNavigationArea.findViewById(R.id.home); 528 mMenuButton = mNavigationArea.findViewById(R.id.menu); 529 mRecentButton = mNavigationArea.findViewById(R.id.recent_apps); 530 mRecentButton.setOnClickListener(mOnClickListener); 531 532 LayoutTransition lt = new LayoutTransition(); 533 lt.setDuration(250); 534 // don't wait for these transitions; we just want icons to fade in/out, not move around 535 lt.setDuration(LayoutTransition.CHANGE_APPEARING, 0); 536 lt.setDuration(LayoutTransition.CHANGE_DISAPPEARING, 0); 537 lt.addTransitionListener(new LayoutTransition.TransitionListener() { 538 public void endTransition(LayoutTransition transition, ViewGroup container, 539 View view, int transitionType) { 540 // ensure the menu button doesn't stick around on the status bar after it's been 541 // removed 542 mBarContents.invalidate(); 543 } 544 public void startTransition(LayoutTransition transition, ViewGroup container, 545 View view, int transitionType) {} 546 }); 547 mNavigationArea.setLayoutTransition(lt); 548 // no multi-touch on the nav buttons 549 mNavigationArea.setMotionEventSplittingEnabled(false); 550 551 // The bar contents buttons 552 mFeedbackIconArea = (ViewGroup)sb.findViewById(R.id.feedbackIconArea); 553 mInputMethodSwitchButton = (InputMethodButton) sb.findViewById(R.id.imeSwitchButton); 554 // Overwrite the lister 555 mInputMethodSwitchButton.setOnClickListener(mOnClickListener); 556 557 mCompatModeButton = (CompatModeButton) sb.findViewById(R.id.compatModeButton); 558 mCompatModeButton.setOnClickListener(mOnClickListener); 559 mCompatModeButton.setVisibility(View.GONE); 560 561 // for redirecting errant bar taps to the IME 562 mFakeSpaceBar = sb.findViewById(R.id.fake_space_bar); 563 564 // "shadows" of the status bar features, for lights-out mode 565 mShadow = sb.findViewById(R.id.bar_shadow); 566 mShadow.setOnTouchListener( 567 new View.OnTouchListener() { 568 public boolean onTouch(View v, MotionEvent ev) { 569 if (ev.getAction() == MotionEvent.ACTION_DOWN) { 570 // even though setting the systemUI visibility below will turn these views 571 // on, we need them to come up faster so that they can catch this motion 572 // event 573 mShadow.setVisibility(View.GONE); 574 mBarContents.setVisibility(View.VISIBLE); 575 576 try { 577 mBarService.setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE); 578 } catch (RemoteException ex) { 579 // system process dead 580 } 581 } 582 return false; 583 } 584 }); 585 586 // tuning parameters 587 final int LIGHTS_GOING_OUT_SYSBAR_DURATION = 600; 588 final int LIGHTS_GOING_OUT_SHADOW_DURATION = 1000; 589 final int LIGHTS_GOING_OUT_SHADOW_DELAY = 500; 590 591 final int LIGHTS_COMING_UP_SYSBAR_DURATION = 200; 592// final int LIGHTS_COMING_UP_SYSBAR_DELAY = 50; 593 final int LIGHTS_COMING_UP_SHADOW_DURATION = 0; 594 595 LayoutTransition xition = new LayoutTransition(); 596 xition.setAnimator(LayoutTransition.APPEARING, 597 ObjectAnimator.ofFloat(null, "alpha", 0.5f, 1f)); 598 xition.setDuration(LayoutTransition.APPEARING, LIGHTS_COMING_UP_SYSBAR_DURATION); 599 xition.setStartDelay(LayoutTransition.APPEARING, 0); 600 xition.setAnimator(LayoutTransition.DISAPPEARING, 601 ObjectAnimator.ofFloat(null, "alpha", 1f, 0f)); 602 xition.setDuration(LayoutTransition.DISAPPEARING, LIGHTS_GOING_OUT_SYSBAR_DURATION); 603 xition.setStartDelay(LayoutTransition.DISAPPEARING, 0); 604 ((ViewGroup)sb.findViewById(R.id.bar_contents_holder)).setLayoutTransition(xition); 605 606 xition = new LayoutTransition(); 607 xition.setAnimator(LayoutTransition.APPEARING, 608 ObjectAnimator.ofFloat(null, "alpha", 0f, 1f)); 609 xition.setDuration(LayoutTransition.APPEARING, LIGHTS_GOING_OUT_SHADOW_DURATION); 610 xition.setStartDelay(LayoutTransition.APPEARING, LIGHTS_GOING_OUT_SHADOW_DELAY); 611 xition.setAnimator(LayoutTransition.DISAPPEARING, 612 ObjectAnimator.ofFloat(null, "alpha", 1f, 0f)); 613 xition.setDuration(LayoutTransition.DISAPPEARING, LIGHTS_COMING_UP_SHADOW_DURATION); 614 xition.setStartDelay(LayoutTransition.DISAPPEARING, 0); 615 ((ViewGroup)sb.findViewById(R.id.bar_shadow_holder)).setLayoutTransition(xition); 616 617 // set the initial view visibility 618 setAreThereNotifications(); 619 620 // receive broadcasts 621 IntentFilter filter = new IntentFilter(); 622 filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 623 filter.addAction(Intent.ACTION_SCREEN_OFF); 624 context.registerReceiver(mBroadcastReceiver, filter); 625 626 return sb; 627 } 628 629 @Override 630 protected WindowManager.LayoutParams getRecentsLayoutParams(LayoutParams layoutParams) { 631 WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 632 (int) mContext.getResources().getDimension(R.dimen.status_bar_recents_width), 633 ViewGroup.LayoutParams.MATCH_PARENT, 634 WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, 635 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 636 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM 637 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH 638 | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, 639 PixelFormat.TRANSLUCENT); 640 lp.gravity = Gravity.BOTTOM | Gravity.LEFT; 641 lp.setTitle("RecentsPanel"); 642 lp.windowAnimations = com.android.internal.R.style.Animation_RecentApplications; 643 lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED 644 | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; 645 646 return lp; 647 } 648 649 protected void updateRecentsPanel() { 650 super.updateRecentsPanel(); 651 mRecentsPanel.setStatusBarView(mStatusBarView); 652 } 653 654 public int getStatusBarHeight() { 655 return mStatusBarView != null ? mStatusBarView.getHeight() 656 : mContext.getResources().getDimensionPixelSize( 657 com.android.internal.R.dimen.navigation_bar_height); 658 } 659 660 protected int getStatusBarGravity() { 661 return Gravity.BOTTOM | Gravity.FILL_HORIZONTAL; 662 } 663 664 public void onBarHeightChanged(int height) { 665 final WindowManager.LayoutParams lp 666 = (WindowManager.LayoutParams)mStatusBarView.getLayoutParams(); 667 if (lp == null) { 668 // haven't been added yet 669 return; 670 } 671 if (lp.height != height) { 672 lp.height = height; 673 final WindowManager wm = WindowManagerImpl.getDefault(); 674 wm.updateViewLayout(mStatusBarView, lp); 675 } 676 } 677 678 @Override 679 protected BaseStatusBar.H createHandler() { 680 return new TabletStatusBar.H(); 681 } 682 683 private class H extends BaseStatusBar.H { 684 public void handleMessage(Message m) { 685 super.handleMessage(m); 686 switch (m.what) { 687 case MSG_OPEN_NOTIFICATION_PEEK: 688 if (DEBUG) Slog.d(TAG, "opening notification peek window; arg=" + m.arg1); 689 690 if (m.arg1 >= 0) { 691 final int N = mNotificationData.size(); 692 693 if (!mNotificationDNDMode) { 694 if (mNotificationPeekIndex >= 0 && mNotificationPeekIndex < N) { 695 NotificationData.Entry entry = mNotificationData.get(N-1-mNotificationPeekIndex); 696 entry.icon.setBackgroundColor(0); 697 mNotificationPeekIndex = -1; 698 mNotificationPeekKey = null; 699 } 700 } 701 702 final int peekIndex = m.arg1; 703 if (peekIndex < N) { 704 //Slog.d(TAG, "loading peek: " + peekIndex); 705 NotificationData.Entry entry = 706 mNotificationDNDMode 707 ? mNotificationDNDDummyEntry 708 : mNotificationData.get(N-1-peekIndex); 709 NotificationData.Entry copy = new NotificationData.Entry( 710 entry.key, 711 entry.notification, 712 entry.icon); 713 inflateViews(copy, mNotificationPeekRow); 714 715 if (mNotificationDNDMode) { 716 copy.content.setOnClickListener(new View.OnClickListener() { 717 public void onClick(View v) { 718 SharedPreferences.Editor editor = Prefs.edit(mContext); 719 editor.putBoolean(Prefs.DO_NOT_DISTURB_PREF, false); 720 editor.apply(); 721 animateCollapse(); 722 visibilityChanged(false); 723 } 724 }); 725 } 726 727 entry.icon.setBackgroundColor(0x20FFFFFF); 728 729// mNotificationPeekRow.setLayoutTransition( 730// peekIndex < mNotificationPeekIndex 731// ? mNotificationPeekScrubLeft 732// : mNotificationPeekScrubRight); 733 734 mNotificationPeekRow.removeAllViews(); 735 mNotificationPeekRow.addView(copy.row); 736 737 mNotificationPeekWindow.setVisibility(View.VISIBLE); 738 mNotificationPanel.show(false, true); 739 740 mNotificationPeekIndex = peekIndex; 741 mNotificationPeekKey = entry.key; 742 } 743 } 744 break; 745 case MSG_CLOSE_NOTIFICATION_PEEK: 746 if (DEBUG) Slog.d(TAG, "closing notification peek window"); 747 mNotificationPeekWindow.setVisibility(View.GONE); 748 mNotificationPeekRow.removeAllViews(); 749 750 final int N = mNotificationData.size(); 751 if (mNotificationPeekIndex >= 0 && mNotificationPeekIndex < N) { 752 NotificationData.Entry entry = 753 mNotificationDNDMode 754 ? mNotificationDNDDummyEntry 755 : mNotificationData.get(N-1-mNotificationPeekIndex); 756 entry.icon.setBackgroundColor(0); 757 } 758 759 mNotificationPeekIndex = -1; 760 mNotificationPeekKey = null; 761 break; 762 case MSG_OPEN_NOTIFICATION_PANEL: 763 if (DEBUG) Slog.d(TAG, "opening notifications panel"); 764 if (!mNotificationPanel.isShowing()) { 765 if (NOTIFICATION_PEEK_ENABLED) { 766 mNotificationPeekWindow.setVisibility(View.GONE); 767 } 768 mNotificationPanel.show(true, true); 769 mNotificationArea.setVisibility(View.INVISIBLE); 770 mTicker.halt(); 771 } 772 break; 773 case MSG_CLOSE_NOTIFICATION_PANEL: 774 if (DEBUG) Slog.d(TAG, "closing notifications panel"); 775 if (mNotificationPanel.isShowing()) { 776 mNotificationPanel.show(false, true); 777 mNotificationArea.setVisibility(View.VISIBLE); 778 } 779 break; 780 case MSG_OPEN_INPUT_METHODS_PANEL: 781 if (DEBUG) Slog.d(TAG, "opening input methods panel"); 782 if (mInputMethodsPanel != null) mInputMethodsPanel.openPanel(); 783 break; 784 case MSG_CLOSE_INPUT_METHODS_PANEL: 785 if (DEBUG) Slog.d(TAG, "closing input methods panel"); 786 if (mInputMethodsPanel != null) mInputMethodsPanel.closePanel(false); 787 break; 788 case MSG_OPEN_COMPAT_MODE_PANEL: 789 if (DEBUG) Slog.d(TAG, "opening compat panel"); 790 if (mCompatModePanel != null) mCompatModePanel.openPanel(); 791 break; 792 case MSG_CLOSE_COMPAT_MODE_PANEL: 793 if (DEBUG) Slog.d(TAG, "closing compat panel"); 794 if (mCompatModePanel != null) mCompatModePanel.closePanel(); 795 break; 796 case MSG_SHOW_CHROME: 797 if (DEBUG) Slog.d(TAG, "hiding shadows (lights on)"); 798 mBarContents.setVisibility(View.VISIBLE); 799 mShadow.setVisibility(View.GONE); 800 mSystemUiVisibility &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE; 801 notifyUiVisibilityChanged(); 802 break; 803 case MSG_HIDE_CHROME: 804 if (DEBUG) Slog.d(TAG, "showing shadows (lights out)"); 805 animateCollapse(); 806 visibilityChanged(false); 807 mBarContents.setVisibility(View.GONE); 808 mShadow.setVisibility(View.VISIBLE); 809 mSystemUiVisibility |= View.SYSTEM_UI_FLAG_LOW_PROFILE; 810 notifyUiVisibilityChanged(); 811 break; 812 case MSG_STOP_TICKER: 813 mTicker.halt(); 814 break; 815 } 816 } 817 } 818 819 public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) { 820 if (DEBUG) Slog.d(TAG, "addIcon(" + slot + ") -> " + icon); 821 } 822 823 public void updateIcon(String slot, int index, int viewIndex, 824 StatusBarIcon old, StatusBarIcon icon) { 825 if (DEBUG) Slog.d(TAG, "updateIcon(" + slot + ") -> " + icon); 826 } 827 828 public void removeIcon(String slot, int index, int viewIndex) { 829 if (DEBUG) Slog.d(TAG, "removeIcon(" + slot + ")"); 830 } 831 832 public void addNotification(IBinder key, StatusBarNotification notification) { 833 if (DEBUG) Slog.d(TAG, "addNotification(" + key + " -> " + notification + ")"); 834 addNotificationViews(key, notification); 835 836 final boolean immersive = isImmersive(); 837 if (false && immersive) { 838 // TODO: immersive mode popups for tablet 839 } else if (notification.notification.fullScreenIntent != null) { 840 // not immersive & a full-screen alert should be shown 841 Slog.w(TAG, "Notification has fullScreenIntent and activity is not immersive;" 842 + " sending fullScreenIntent"); 843 try { 844 notification.notification.fullScreenIntent.send(); 845 } catch (PendingIntent.CanceledException e) { 846 } 847 } else { 848 tick(key, notification, true); 849 } 850 851 setAreThereNotifications(); 852 } 853 854 public void updateNotification(IBinder key, StatusBarNotification notification) { 855 if (DEBUG) Slog.d(TAG, "updateNotification(" + key + " -> " + notification + ")"); 856 857 final NotificationData.Entry oldEntry = mNotificationData.findByKey(key); 858 if (oldEntry == null) { 859 Slog.w(TAG, "updateNotification for unknown key: " + key); 860 return; 861 } 862 863 final StatusBarNotification oldNotification = oldEntry.notification; 864 865 // XXX: modify when we do something more intelligent with the two content views 866 final RemoteViews oldContentView = (oldNotification.notification.bigContentView != null) 867 ? oldNotification.notification.bigContentView 868 : oldNotification.notification.contentView; 869 final RemoteViews contentView = (notification.notification.bigContentView != null) 870 ? notification.notification.bigContentView 871 : notification.notification.contentView; 872 873 if (DEBUG) { 874 Slog.d(TAG, "old notification: when=" + oldNotification.notification.when 875 + " ongoing=" + oldNotification.isOngoing() 876 + " expanded=" + oldEntry.expanded 877 + " contentView=" + oldContentView 878 + " rowParent=" + oldEntry.row.getParent()); 879 Slog.d(TAG, "new notification: when=" + notification.notification.when 880 + " ongoing=" + oldNotification.isOngoing() 881 + " contentView=" + contentView); 882 } 883 884 // Can we just reapply the RemoteViews in place? If when didn't change, the order 885 // didn't change. 886 boolean contentsUnchanged = oldEntry.expanded != null 887 && contentView != null && oldContentView != null 888 && contentView.getPackage() != null 889 && oldContentView.getPackage() != null 890 && oldContentView.getPackage().equals(contentView.getPackage()) 891 && oldContentView.getLayoutId() == contentView.getLayoutId(); 892 ViewGroup rowParent = (ViewGroup) oldEntry.row.getParent(); 893 boolean orderUnchanged = notification.notification.when==oldNotification.notification.when 894 && notification.score == oldNotification.score; 895 // score now encompasses/supersedes isOngoing() 896 boolean updateTicker = notification.notification.tickerText != null 897 && !TextUtils.equals(notification.notification.tickerText, 898 oldEntry.notification.notification.tickerText); 899 boolean isLastAnyway = rowParent.indexOfChild(oldEntry.row) == rowParent.getChildCount()-1; 900 if (contentsUnchanged && (orderUnchanged || isLastAnyway)) { 901 if (DEBUG) Slog.d(TAG, "reusing notification for key: " + key); 902 oldEntry.notification = notification; 903 try { 904 // Reapply the RemoteViews 905 contentView.reapply(mContext, oldEntry.content); 906 // update the contentIntent 907 final PendingIntent contentIntent = notification.notification.contentIntent; 908 if (contentIntent != null) { 909 final View.OnClickListener listener = makeClicker(contentIntent, 910 notification.pkg, notification.tag, notification.id); 911 oldEntry.content.setOnClickListener(listener); 912 } else { 913 oldEntry.content.setOnClickListener(null); 914 } 915 // Update the icon. 916 final StatusBarIcon ic = new StatusBarIcon(notification.pkg, 917 notification.notification.icon, notification.notification.iconLevel, 918 notification.notification.number, 919 notification.notification.tickerText); 920 if (!oldEntry.icon.set(ic)) { 921 handleNotificationError(key, notification, "Couldn't update icon: " + ic); 922 return; 923 } 924 925 if (NOTIFICATION_PEEK_ENABLED && key == mNotificationPeekKey) { 926 // must update the peek window 927 Message peekMsg = mHandler.obtainMessage(MSG_OPEN_NOTIFICATION_PEEK); 928 peekMsg.arg1 = mNotificationPeekIndex; 929 mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PEEK); 930 mHandler.sendMessage(peekMsg); 931 } 932 } 933 catch (RuntimeException e) { 934 // It failed to add cleanly. Log, and remove the view from the panel. 935 Slog.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e); 936 removeNotificationViews(key); 937 addNotificationViews(key, notification); 938 } 939 } else { 940 if (DEBUG) Slog.d(TAG, "not reusing notification for key: " + key); 941 removeNotificationViews(key); 942 addNotificationViews(key, notification); 943 } 944 945 // Restart the ticker if it's still running 946 if (updateTicker) { 947 mTicker.halt(); 948 tick(key, notification, false); 949 } 950 951 setAreThereNotifications(); 952 } 953 954 public void removeNotification(IBinder key) { 955 if (DEBUG) Slog.d(TAG, "removeNotification(" + key + ")"); 956 removeNotificationViews(key); 957 mTicker.remove(key); 958 setAreThereNotifications(); 959 } 960 961 public void showClock(boolean show) { 962 View clock = mBarContents.findViewById(R.id.clock); 963 View network_text = mBarContents.findViewById(R.id.network_text); 964 if (clock != null) { 965 clock.setVisibility(show ? View.VISIBLE : View.GONE); 966 } 967 if (network_text != null) { 968 network_text.setVisibility((!show) ? View.VISIBLE : View.GONE); 969 } 970 } 971 972 public void disable(int state) { 973 int old = mDisabled; 974 int diff = state ^ old; 975 mDisabled = state; 976 977 // act accordingly 978 if ((diff & StatusBarManager.DISABLE_CLOCK) != 0) { 979 boolean show = (state & StatusBarManager.DISABLE_CLOCK) == 0; 980 Slog.i(TAG, "DISABLE_CLOCK: " + (show ? "no" : "yes")); 981 showClock(show); 982 } 983 if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) { 984 boolean show = (state & StatusBarManager.DISABLE_SYSTEM_INFO) == 0; 985 Slog.i(TAG, "DISABLE_SYSTEM_INFO: " + (show ? "no" : "yes")); 986 mNotificationTrigger.setVisibility(show ? View.VISIBLE : View.GONE); 987 } 988 if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) { 989 if ((state & StatusBarManager.DISABLE_EXPAND) != 0) { 990 Slog.i(TAG, "DISABLE_EXPAND: yes"); 991 animateCollapse(); 992 visibilityChanged(false); 993 } 994 } 995 if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { 996 mNotificationDNDMode = Prefs.read(mContext) 997 .getBoolean(Prefs.DO_NOT_DISTURB_PREF, Prefs.DO_NOT_DISTURB_DEFAULT); 998 999 if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { 1000 Slog.i(TAG, "DISABLE_NOTIFICATION_ICONS: yes" + (mNotificationDNDMode?" (DND)":"")); 1001 mTicker.halt(); 1002 } else { 1003 Slog.i(TAG, "DISABLE_NOTIFICATION_ICONS: no" + (mNotificationDNDMode?" (DND)":"")); 1004 } 1005 1006 // refresh icons to show either notifications or the DND message 1007 reloadAllNotificationIcons(); 1008 } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) { 1009 if ((state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) { 1010 mTicker.halt(); 1011 } 1012 } 1013 if ((diff & (StatusBarManager.DISABLE_RECENT 1014 | StatusBarManager.DISABLE_BACK 1015 | StatusBarManager.DISABLE_HOME)) != 0) { 1016 setNavigationVisibility(state); 1017 1018 if ((state & StatusBarManager.DISABLE_RECENT) != 0) { 1019 // close recents if it's visible 1020 mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL); 1021 mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL); 1022 } 1023 } 1024 } 1025 1026 private void setNavigationVisibility(int visibility) { 1027 boolean disableHome = ((visibility & StatusBarManager.DISABLE_HOME) != 0); 1028 boolean disableRecent = ((visibility & StatusBarManager.DISABLE_RECENT) != 0); 1029 boolean disableBack = ((visibility & StatusBarManager.DISABLE_BACK) != 0); 1030 1031 mBackButton.setVisibility(disableBack ? View.INVISIBLE : View.VISIBLE); 1032 mHomeButton.setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE); 1033 mRecentButton.setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE); 1034 1035 mInputMethodSwitchButton.setScreenLocked( 1036 (visibility & StatusBarManager.DISABLE_SYSTEM_INFO) != 0); 1037 } 1038 1039 private boolean hasTicker(Notification n) { 1040 return n.tickerView != null || !TextUtils.isEmpty(n.tickerText); 1041 } 1042 1043 private void tick(IBinder key, StatusBarNotification n, boolean firstTime) { 1044 // Don't show the ticker when the windowshade is open. 1045 if (mNotificationPanel.isShowing()) { 1046 return; 1047 } 1048 // If they asked for FLAG_ONLY_ALERT_ONCE, then only show this notification 1049 // if it's a new notification. 1050 if (!firstTime && (n.notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) { 1051 return; 1052 } 1053 // Show the ticker if one is requested. Also don't do this 1054 // until status bar window is attached to the window manager, 1055 // because... well, what's the point otherwise? And trying to 1056 // run a ticker without being attached will crash! 1057 if (hasTicker(n.notification) && mStatusBarView.getWindowToken() != null) { 1058 if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS 1059 | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) { 1060 mTicker.add(key, n); 1061 mFeedbackIconArea.setVisibility(View.GONE); 1062 } 1063 } 1064 } 1065 1066 // called by TabletTicker when it's done with all queued ticks 1067 public void doneTicking() { 1068 mFeedbackIconArea.setVisibility(View.VISIBLE); 1069 } 1070 1071 public void animateExpand() { 1072 if (NOTIFICATION_PEEK_ENABLED) { 1073 mHandler.removeMessages(MSG_CLOSE_NOTIFICATION_PEEK); 1074 mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PEEK); 1075 mHandler.sendEmptyMessage(MSG_CLOSE_NOTIFICATION_PEEK); 1076 } 1077 mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PANEL); 1078 mHandler.sendEmptyMessage(MSG_OPEN_NOTIFICATION_PANEL); 1079 } 1080 1081 public void animateCollapse() { 1082 animateCollapse(false); 1083 } 1084 1085 private void animateCollapse(boolean excludeRecents) { 1086 mHandler.removeMessages(MSG_CLOSE_NOTIFICATION_PANEL); 1087 mHandler.sendEmptyMessage(MSG_CLOSE_NOTIFICATION_PANEL); 1088 if (!excludeRecents) { 1089 mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL); 1090 mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL); 1091 } 1092 mHandler.removeMessages(MSG_CLOSE_INPUT_METHODS_PANEL); 1093 mHandler.sendEmptyMessage(MSG_CLOSE_INPUT_METHODS_PANEL); 1094 mHandler.removeMessages(MSG_CLOSE_COMPAT_MODE_PANEL); 1095 mHandler.sendEmptyMessage(MSG_CLOSE_COMPAT_MODE_PANEL); 1096 if (NOTIFICATION_PEEK_ENABLED) { 1097 mHandler.removeMessages(MSG_CLOSE_NOTIFICATION_PEEK); 1098 mHandler.sendEmptyMessage(MSG_CLOSE_NOTIFICATION_PEEK); 1099 } 1100 } 1101 1102 @Override // CommandQueue 1103 public void setNavigationIconHints(int hints) { 1104 if (hints == mNavigationIconHints) return; 1105 1106 if (DEBUG) { 1107 android.widget.Toast.makeText(mContext, 1108 "Navigation icon hints = " + hints, 1109 500).show(); 1110 } 1111 1112 mNavigationIconHints = hints; 1113 1114 mBackButton.setAlpha( 1115 (0 != (hints & StatusBarManager.NAVIGATION_HINT_BACK_NOP)) ? 0.5f : 1.0f); 1116 mHomeButton.setAlpha( 1117 (0 != (hints & StatusBarManager.NAVIGATION_HINT_HOME_NOP)) ? 0.5f : 1.0f); 1118 mRecentButton.setAlpha( 1119 (0 != (hints & StatusBarManager.NAVIGATION_HINT_RECENT_NOP)) ? 0.5f : 1.0f); 1120 1121 mBackButton.setImageResource( 1122 (0 != (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT)) 1123 ? R.drawable.ic_sysbar_back_ime 1124 : R.drawable.ic_sysbar_back); 1125 } 1126 1127 private void notifyUiVisibilityChanged() { 1128 try { 1129 mWindowManager.statusBarVisibilityChanged(mSystemUiVisibility); 1130 } catch (RemoteException ex) { 1131 } 1132 } 1133 1134 @Override // CommandQueue 1135 public void setSystemUiVisibility(int vis, int mask) { 1136 final int oldVal = mSystemUiVisibility; 1137 final int newVal = (oldVal&~mask) | (vis&mask); 1138 final int diff = newVal ^ oldVal; 1139 1140 if (diff != 0) { 1141 mSystemUiVisibility = newVal; 1142 1143 if (0 != (diff & View.SYSTEM_UI_FLAG_LOW_PROFILE)) { 1144 mHandler.removeMessages(MSG_HIDE_CHROME); 1145 mHandler.removeMessages(MSG_SHOW_CHROME); 1146 mHandler.sendEmptyMessage(0 == (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) 1147 ? MSG_SHOW_CHROME : MSG_HIDE_CHROME); 1148 } 1149 1150 notifyUiVisibilityChanged(); 1151 } 1152 } 1153 1154 public void setLightsOn(boolean on) { 1155 // Policy note: if the frontmost activity needs the menu key, we assume it is a legacy app 1156 // that can't handle lights-out mode. 1157 if (mMenuButton.getVisibility() == View.VISIBLE) { 1158 on = true; 1159 } 1160 1161 Slog.v(TAG, "setLightsOn(" + on + ")"); 1162 if (on) { 1163 setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE); 1164 } else { 1165 setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, View.SYSTEM_UI_FLAG_LOW_PROFILE); 1166 } 1167 } 1168 1169 public void topAppWindowChanged(boolean showMenu) { 1170 if (DEBUG) { 1171 Slog.d(TAG, (showMenu?"showing":"hiding") + " the MENU button"); 1172 } 1173 mMenuButton.setVisibility(showMenu ? View.VISIBLE : View.GONE); 1174 1175 // See above re: lights-out policy for legacy apps. 1176 if (showMenu) setLightsOn(true); 1177 1178 mCompatModeButton.refresh(); 1179 if (mCompatModeButton.getVisibility() == View.VISIBLE) { 1180 if (DEBUG_COMPAT_HELP 1181 || ! Prefs.read(mContext).getBoolean(Prefs.SHOWN_COMPAT_MODE_HELP, false)) { 1182 showCompatibilityHelp(); 1183 } 1184 } else { 1185 hideCompatibilityHelp(); 1186 mCompatModePanel.closePanel(); 1187 } 1188 } 1189 1190 private void showCompatibilityHelp() { 1191 if (mCompatibilityHelpDialog != null) { 1192 return; 1193 } 1194 1195 mCompatibilityHelpDialog = View.inflate(mContext, R.layout.compat_mode_help, null); 1196 View button = mCompatibilityHelpDialog.findViewById(R.id.button); 1197 1198 button.setOnClickListener(new View.OnClickListener() { 1199 @Override 1200 public void onClick(View v) { 1201 hideCompatibilityHelp(); 1202 SharedPreferences.Editor editor = Prefs.edit(mContext); 1203 editor.putBoolean(Prefs.SHOWN_COMPAT_MODE_HELP, true); 1204 editor.apply(); 1205 } 1206 }); 1207 1208 WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 1209 ViewGroup.LayoutParams.MATCH_PARENT, 1210 ViewGroup.LayoutParams.MATCH_PARENT, 1211 WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG, 1212 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 1213 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 1214 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, 1215 PixelFormat.TRANSLUCENT); 1216 lp.setTitle("CompatibilityModeDialog"); 1217 lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED 1218 | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; 1219 lp.windowAnimations = com.android.internal.R.style.Animation_ZoomButtons; // simple fade 1220 1221 WindowManagerImpl.getDefault().addView(mCompatibilityHelpDialog, lp); 1222 } 1223 1224 private void hideCompatibilityHelp() { 1225 if (mCompatibilityHelpDialog != null) { 1226 WindowManagerImpl.getDefault().removeView(mCompatibilityHelpDialog); 1227 mCompatibilityHelpDialog = null; 1228 } 1229 } 1230 1231 public void setImeWindowStatus(IBinder token, int vis, int backDisposition) { 1232 mInputMethodSwitchButton.setImeWindowStatus(token, 1233 (vis & InputMethodService.IME_ACTIVE) != 0); 1234 updateNotificationIcons(); 1235 mInputMethodsPanel.setImeToken(token); 1236 1237 boolean altBack = (backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS) 1238 || ((vis & InputMethodService.IME_VISIBLE) != 0); 1239 mAltBackButtonEnabledForIme = altBack; 1240 1241 mCommandQueue.setNavigationIconHints( 1242 altBack ? (mNavigationIconHints | StatusBarManager.NAVIGATION_HINT_BACK_ALT) 1243 : (mNavigationIconHints & ~StatusBarManager.NAVIGATION_HINT_BACK_ALT)); 1244 1245 if (FAKE_SPACE_BAR) { 1246 mFakeSpaceBar.setVisibility(((vis & InputMethodService.IME_VISIBLE) != 0) 1247 ? View.VISIBLE : View.GONE); 1248 } 1249 } 1250 1251 @Override 1252 public void onRecentsPanelVisibilityChanged(boolean visible) { 1253 boolean altBack = visible || mAltBackButtonEnabledForIme; 1254 mCommandQueue.setNavigationIconHints( 1255 altBack ? (mNavigationIconHints | StatusBarManager.NAVIGATION_HINT_BACK_ALT) 1256 : (mNavigationIconHints & ~StatusBarManager.NAVIGATION_HINT_BACK_ALT)); 1257 } 1258 1259 @Override 1260 public void setHardKeyboardStatus(boolean available, boolean enabled) { 1261 if (DEBUG) { 1262 Slog.d(TAG, "Set hard keyboard status: available=" + available 1263 + ", enabled=" + enabled); 1264 } 1265 mInputMethodSwitchButton.setHardKeyboardStatus(available); 1266 updateNotificationIcons(); 1267 mInputMethodsPanel.setHardKeyboardStatus(available, enabled); 1268 } 1269 1270 @Override 1271 public void onHardKeyboardEnabledChange(boolean enabled) { 1272 try { 1273 mBarService.setHardKeyboardEnabled(enabled); 1274 } catch (RemoteException ex) { 1275 } 1276 } 1277 1278 private boolean isImmersive() { 1279 try { 1280 return ActivityManagerNative.getDefault().isTopActivityImmersive(); 1281 //Slog.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive")); 1282 } catch (RemoteException ex) { 1283 // the end is nigh 1284 return false; 1285 } 1286 } 1287 1288 private void setAreThereNotifications() { 1289 if (mNotificationPanel != null) { 1290 mNotificationPanel.setClearable(mNotificationData.hasClearableItems()); 1291 } 1292 } 1293 1294 /** 1295 * Cancel this notification and tell the status bar service about the failure. Hold no locks. 1296 */ 1297 void handleNotificationError(IBinder key, StatusBarNotification n, String message) { 1298 removeNotification(key); 1299 try { 1300 mBarService.onNotificationError(n.pkg, n.tag, n.id, n.uid, n.initialPid, message); 1301 } catch (RemoteException ex) { 1302 // The end is nigh. 1303 } 1304 } 1305 1306 private View.OnClickListener mOnClickListener = new View.OnClickListener() { 1307 public void onClick(View v) { 1308 if (v == mRecentButton) { 1309 onClickRecentButton(); 1310 } else if (v == mInputMethodSwitchButton) { 1311 onClickInputMethodSwitchButton(); 1312 } else if (v == mCompatModeButton) { 1313 onClickCompatModeButton(); 1314 } 1315 } 1316 }; 1317 1318 public void onClickRecentButton() { 1319 if (DEBUG) Slog.d(TAG, "clicked recent apps; disabled=" + mDisabled); 1320 if ((mDisabled & StatusBarManager.DISABLE_EXPAND) == 0) { 1321 int msg = (mRecentsPanel.getVisibility() == View.VISIBLE) 1322 ? MSG_CLOSE_RECENTS_PANEL : MSG_OPEN_RECENTS_PANEL; 1323 mHandler.removeMessages(msg); 1324 mHandler.sendEmptyMessage(msg); 1325 } 1326 } 1327 1328 public void onClickInputMethodSwitchButton() { 1329 if (DEBUG) Slog.d(TAG, "clicked input methods panel; disabled=" + mDisabled); 1330 int msg = (mInputMethodsPanel.getVisibility() == View.GONE) ? 1331 MSG_OPEN_INPUT_METHODS_PANEL : MSG_CLOSE_INPUT_METHODS_PANEL; 1332 mHandler.removeMessages(msg); 1333 mHandler.sendEmptyMessage(msg); 1334 } 1335 1336 public void onClickCompatModeButton() { 1337 int msg = (mCompatModePanel.getVisibility() == View.GONE) ? 1338 MSG_OPEN_COMPAT_MODE_PANEL : MSG_CLOSE_COMPAT_MODE_PANEL; 1339 mHandler.removeMessages(msg); 1340 mHandler.sendEmptyMessage(msg); 1341 } 1342 1343 StatusBarNotification removeNotificationViews(IBinder key) { 1344 NotificationData.Entry entry = mNotificationData.remove(key); 1345 if (entry == null) { 1346 Slog.w(TAG, "removeNotification for unknown key: " + key); 1347 return null; 1348 } 1349 // Remove the expanded view. 1350 ViewGroup rowParent = (ViewGroup)entry.row.getParent(); 1351 if (rowParent != null) rowParent.removeView(entry.row); 1352 1353 if (NOTIFICATION_PEEK_ENABLED && key == mNotificationPeekKey) { 1354 // must close the peek as well, since it's gone 1355 mHandler.sendEmptyMessage(MSG_CLOSE_NOTIFICATION_PEEK); 1356 } 1357 // Remove the icon. 1358// ViewGroup iconParent = (ViewGroup)entry.icon.getParent(); 1359// if (iconParent != null) iconParent.removeView(entry.icon); 1360 updateNotificationIcons(); 1361 1362 return entry.notification; 1363 } 1364 1365 private class NotificationTriggerTouchListener implements View.OnTouchListener { 1366 VelocityTracker mVT; 1367 float mInitialTouchX, mInitialTouchY; 1368 int mTouchSlop; 1369 1370 public NotificationTriggerTouchListener() { 1371 mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); 1372 } 1373 1374 private Runnable mHiliteOnR = new Runnable() { public void run() { 1375 mNotificationArea.setBackgroundResource( 1376 com.android.internal.R.drawable.list_selector_pressed_holo_dark); 1377 }}; 1378 public void hilite(final boolean on) { 1379 if (on) { 1380 mNotificationArea.postDelayed(mHiliteOnR, 100); 1381 } else { 1382 mNotificationArea.removeCallbacks(mHiliteOnR); 1383 mNotificationArea.setBackgroundDrawable(null); 1384 } 1385 } 1386 1387 public boolean onTouch(View v, MotionEvent event) { 1388// Slog.d(TAG, String.format("touch: (%.1f, %.1f) initial: (%.1f, %.1f)", 1389// event.getX(), 1390// event.getY(), 1391// mInitialTouchX, 1392// mInitialTouchY)); 1393 1394 if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) { 1395 return true; 1396 } 1397 1398 final int action = event.getAction(); 1399 switch (action) { 1400 case MotionEvent.ACTION_DOWN: 1401 mVT = VelocityTracker.obtain(); 1402 mInitialTouchX = event.getX(); 1403 mInitialTouchY = event.getY(); 1404 hilite(true); 1405 // fall through 1406 case MotionEvent.ACTION_OUTSIDE: 1407 case MotionEvent.ACTION_MOVE: 1408 // check for fling 1409 if (mVT != null) { 1410 mVT.addMovement(event); 1411 mVT.computeCurrentVelocity(1000); // pixels per second 1412 // require a little more oomph once we're already in peekaboo mode 1413 if (mVT.getYVelocity() < -mNotificationFlingVelocity) { 1414 animateExpand(); 1415 visibilityChanged(true); 1416 hilite(false); 1417 mVT.recycle(); 1418 mVT = null; 1419 } 1420 } 1421 return true; 1422 case MotionEvent.ACTION_UP: 1423 case MotionEvent.ACTION_CANCEL: 1424 hilite(false); 1425 if (mVT != null) { 1426 if (action == MotionEvent.ACTION_UP 1427 // was this a sloppy tap? 1428 && Math.abs(event.getX() - mInitialTouchX) < mTouchSlop 1429 && Math.abs(event.getY() - mInitialTouchY) < (mTouchSlop / 3) 1430 // dragging off the bottom doesn't count 1431 && (int)event.getY() < v.getBottom()) { 1432 animateExpand(); 1433 visibilityChanged(true); 1434 v.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); 1435 v.playSoundEffect(SoundEffectConstants.CLICK); 1436 } 1437 1438 mVT.recycle(); 1439 mVT = null; 1440 return true; 1441 } 1442 } 1443 return false; 1444 } 1445 } 1446 1447 public void resetNotificationPeekFadeTimer() { 1448 if (DEBUG) { 1449 Slog.d(TAG, "setting peek fade timer for " + NOTIFICATION_PEEK_FADE_DELAY 1450 + "ms from now"); 1451 } 1452 mHandler.removeMessages(MSG_CLOSE_NOTIFICATION_PEEK); 1453 mHandler.sendEmptyMessageDelayed(MSG_CLOSE_NOTIFICATION_PEEK, 1454 NOTIFICATION_PEEK_FADE_DELAY); 1455 } 1456 1457 private class NotificationIconTouchListener implements View.OnTouchListener { 1458 VelocityTracker mVT; 1459 int mPeekIndex; 1460 float mInitialTouchX, mInitialTouchY; 1461 int mTouchSlop; 1462 1463 public NotificationIconTouchListener() { 1464 mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); 1465 } 1466 1467 public boolean onTouch(View v, MotionEvent event) { 1468 boolean peeking = mNotificationPeekWindow.getVisibility() != View.GONE; 1469 boolean panelShowing = mNotificationPanel.isShowing(); 1470 if (panelShowing) return false; 1471 1472 int numIcons = mIconLayout.getChildCount(); 1473 int newPeekIndex = (int)(event.getX() * numIcons / mIconLayout.getWidth()); 1474 if (newPeekIndex > numIcons - 1) newPeekIndex = numIcons - 1; 1475 else if (newPeekIndex < 0) newPeekIndex = 0; 1476 1477 final int action = event.getAction(); 1478 switch (action) { 1479 case MotionEvent.ACTION_DOWN: 1480 mVT = VelocityTracker.obtain(); 1481 mInitialTouchX = event.getX(); 1482 mInitialTouchY = event.getY(); 1483 mPeekIndex = -1; 1484 1485 // fall through 1486 case MotionEvent.ACTION_OUTSIDE: 1487 case MotionEvent.ACTION_MOVE: 1488 // peek and switch icons if necessary 1489 1490 if (newPeekIndex != mPeekIndex) { 1491 mPeekIndex = newPeekIndex; 1492 1493 if (DEBUG) Slog.d(TAG, "will peek at notification #" + mPeekIndex); 1494 Message peekMsg = mHandler.obtainMessage(MSG_OPEN_NOTIFICATION_PEEK); 1495 peekMsg.arg1 = mPeekIndex; 1496 1497 mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PEEK); 1498 1499 if (peeking) { 1500 // no delay if we're scrubbing left-right 1501 mHandler.sendMessage(peekMsg); 1502 } else { 1503 // wait for fling 1504 mHandler.sendMessageDelayed(peekMsg, NOTIFICATION_PEEK_HOLD_THRESH); 1505 } 1506 } 1507 1508 // check for fling 1509 if (mVT != null) { 1510 mVT.addMovement(event); 1511 mVT.computeCurrentVelocity(1000); // pixels per second 1512 // require a little more oomph once we're already in peekaboo mode 1513 if (!panelShowing && ( 1514 (peeking && mVT.getYVelocity() < -mNotificationFlingVelocity*3) 1515 || (mVT.getYVelocity() < -mNotificationFlingVelocity))) { 1516 mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PEEK); 1517 mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PANEL); 1518 mHandler.sendEmptyMessage(MSG_CLOSE_NOTIFICATION_PEEK); 1519 mHandler.sendEmptyMessage(MSG_OPEN_NOTIFICATION_PANEL); 1520 } 1521 } 1522 return true; 1523 case MotionEvent.ACTION_UP: 1524 case MotionEvent.ACTION_CANCEL: 1525 mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PEEK); 1526 if (!peeking) { 1527 if (action == MotionEvent.ACTION_UP 1528 // was this a sloppy tap? 1529 && Math.abs(event.getX() - mInitialTouchX) < mTouchSlop 1530 && Math.abs(event.getY() - mInitialTouchY) < (mTouchSlop / 3) 1531 // dragging off the bottom doesn't count 1532 && (int)event.getY() < v.getBottom()) { 1533 Message peekMsg = mHandler.obtainMessage(MSG_OPEN_NOTIFICATION_PEEK); 1534 peekMsg.arg1 = mPeekIndex; 1535 mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PEEK); 1536 mHandler.sendMessage(peekMsg); 1537 1538 v.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); 1539 v.playSoundEffect(SoundEffectConstants.CLICK); 1540 1541 peeking = true; // not technically true yet, but the next line will run 1542 } 1543 } 1544 1545 if (peeking) { 1546 resetNotificationPeekFadeTimer(); 1547 } 1548 1549 mVT.recycle(); 1550 mVT = null; 1551 return true; 1552 } 1553 return false; 1554 } 1555 } 1556 1557 StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) { 1558 if (DEBUG) { 1559 Slog.d(TAG, "addNotificationViews(key=" + key + ", notification=" + notification); 1560 } 1561 // Construct the icon. 1562 final StatusBarIconView iconView = new StatusBarIconView(mContext, 1563 notification.pkg + "/0x" + Integer.toHexString(notification.id), 1564 notification.notification); 1565 iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE); 1566 1567 final StatusBarIcon ic = new StatusBarIcon(notification.pkg, 1568 notification.notification.icon, 1569 notification.notification.iconLevel, 1570 notification.notification.number, 1571 notification.notification.tickerText); 1572 if (!iconView.set(ic)) { 1573 handleNotificationError(key, notification, "Couldn't attach StatusBarIcon: " + ic); 1574 return null; 1575 } 1576 // Construct the expanded view. 1577 NotificationData.Entry entry = new NotificationData.Entry(key, notification, iconView); 1578 if (!inflateViews(entry, mPile)) { 1579 handleNotificationError(key, notification, "Couldn't expand RemoteViews for: " 1580 + notification); 1581 return null; 1582 } 1583 1584 // Add the icon. 1585 int pos = mNotificationData.add(entry); 1586 if (DEBUG) { 1587 Slog.d(TAG, "addNotificationViews: added at " + pos); 1588 } 1589 updateNotificationIcons(); 1590 1591 return iconView; 1592 } 1593 1594 private void reloadAllNotificationIcons() { 1595 if (mIconLayout == null) return; 1596 mIconLayout.removeAllViews(); 1597 updateNotificationIcons(); 1598 } 1599 1600 private void updateNotificationIcons() { 1601 // XXX: need to implement a new limited linear layout class 1602 // to avoid removing & readding everything 1603 1604 if (mIconLayout == null) return; 1605 1606 // first, populate the main notification panel 1607 loadNotificationPanel(); 1608 1609 final LinearLayout.LayoutParams params 1610 = new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight); 1611 1612 // alternate behavior in DND mode 1613 if (mNotificationDNDMode) { 1614 if (mIconLayout.getChildCount() == 0) { 1615 final Notification dndNotification = new Notification.Builder(mContext) 1616 .setContentTitle(mContext.getText(R.string.notifications_off_title)) 1617 .setContentText(mContext.getText(R.string.notifications_off_text)) 1618 .setSmallIcon(R.drawable.ic_notification_dnd) 1619 .setOngoing(true) 1620 .getNotification(); 1621 1622 final StatusBarIconView iconView = new StatusBarIconView(mContext, "_dnd", 1623 dndNotification); 1624 iconView.setImageResource(R.drawable.ic_notification_dnd); 1625 iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE); 1626 iconView.setPadding(mIconHPadding, 0, mIconHPadding, 0); 1627 1628 mNotificationDNDDummyEntry = new NotificationData.Entry( 1629 null, 1630 new StatusBarNotification("", 0, "", 0, 0, Notification.PRIORITY_MAX, dndNotification), 1631 iconView); 1632 1633 mIconLayout.addView(iconView, params); 1634 } 1635 1636 return; 1637 } else if (0 != (mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS)) { 1638 // if icons are disabled but we're not in DND mode, this is probably Setup and we should 1639 // just leave the area totally empty 1640 return; 1641 } 1642 1643 int N = mNotificationData.size(); 1644 1645 if (DEBUG) { 1646 Slog.d(TAG, "refreshing icons: " + N + " notifications, mIconLayout=" + mIconLayout); 1647 } 1648 1649 ArrayList<View> toShow = new ArrayList<View>(); 1650 1651 // Extra Special Icons 1652 // The IME switcher and compatibility mode icons take the place of notifications. You didn't 1653 // need to see all those new emails, did you? 1654 int maxNotificationIconsCount = mMaxNotificationIcons; 1655 if (mInputMethodSwitchButton.getVisibility() != View.GONE) maxNotificationIconsCount --; 1656 if (mCompatModeButton.getVisibility() != View.GONE) maxNotificationIconsCount --; 1657 1658 for (int i=0; i< maxNotificationIconsCount; i++) { 1659 if (i>=N) break; 1660 toShow.add(mNotificationData.get(N-i-1).icon); 1661 } 1662 1663 ArrayList<View> toRemove = new ArrayList<View>(); 1664 for (int i=0; i<mIconLayout.getChildCount(); i++) { 1665 View child = mIconLayout.getChildAt(i); 1666 if (!toShow.contains(child)) { 1667 toRemove.add(child); 1668 } 1669 } 1670 1671 for (View remove : toRemove) { 1672 mIconLayout.removeView(remove); 1673 } 1674 1675 for (int i=0; i<toShow.size(); i++) { 1676 View v = toShow.get(i); 1677 v.setPadding(mIconHPadding, 0, mIconHPadding, 0); 1678 if (v.getParent() == null) { 1679 mIconLayout.addView(v, i, params); 1680 } 1681 } 1682 } 1683 1684 private void loadNotificationPanel() { 1685 int N = mNotificationData.size(); 1686 1687 ArrayList<View> toShow = new ArrayList<View>(); 1688 1689 for (int i=0; i<N; i++) { 1690 View row = mNotificationData.get(N-i-1).row; 1691 toShow.add(row); 1692 } 1693 1694 ArrayList<View> toRemove = new ArrayList<View>(); 1695 for (int i=0; i<mPile.getChildCount(); i++) { 1696 View child = mPile.getChildAt(i); 1697 if (!toShow.contains(child)) { 1698 toRemove.add(child); 1699 } 1700 } 1701 1702 for (View remove : toRemove) { 1703 mPile.removeView(remove); 1704 } 1705 1706 for (int i=0; i<toShow.size(); i++) { 1707 View v = toShow.get(i); 1708 if (v.getParent() == null) { 1709 mPile.addView(v, N-1-i); // the notification panel has newest at the bottom 1710 } 1711 } 1712 1713 mNotificationPanel.setNotificationCount(N); 1714 } 1715 1716 @Override 1717 protected void workAroundBadLayerDrawableOpacity(View v) { 1718 Drawable bgd = v.getBackground(); 1719 if (!(bgd instanceof LayerDrawable)) return; 1720 1721 LayerDrawable d = (LayerDrawable) bgd; 1722 v.setBackgroundDrawable(null); 1723 d.setOpacity(PixelFormat.TRANSLUCENT); 1724 v.setBackgroundDrawable(d); 1725 } 1726 1727 public void clearAll() { 1728 try { 1729 mBarService.onClearAllNotifications(); 1730 } catch (RemoteException ex) { 1731 // system process is dead if we're here. 1732 } 1733 animateCollapse(); 1734 visibilityChanged(false); 1735 } 1736 1737 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 1738 public void onReceive(Context context, Intent intent) { 1739 String action = intent.getAction(); 1740 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action) 1741 || Intent.ACTION_SCREEN_OFF.equals(action)) { 1742 boolean excludeRecents = false; 1743 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { 1744 String reason = intent.getStringExtra("reason"); 1745 if (reason != null) { 1746 excludeRecents = reason.equals("recentapps"); 1747 } 1748 } 1749 if (Intent.ACTION_SCREEN_OFF.equals(action)) { 1750 // If we're turning the screen off, we want to hide the 1751 // recents panel with no animation 1752 // TODO: hide other things, like the notification tray, 1753 // with no animation as well 1754 mRecentsPanel.show(false, false); 1755 excludeRecents = true; 1756 } 1757 animateCollapse(excludeRecents); 1758 } 1759 } 1760 }; 1761 1762 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1763 pw.print("mDisabled=0x"); 1764 pw.println(Integer.toHexString(mDisabled)); 1765 pw.println("mNetworkController:"); 1766 mNetworkController.dump(fd, pw, args); 1767 } 1768 1769} 1770 1771 1772