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