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