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