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