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