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