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