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