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