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