1 /* 2 * Copyright (C) 2012 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.phone; 18 19 import android.annotation.ColorInt; 20 import android.annotation.DrawableRes; 21 import android.annotation.LayoutRes; 22 import android.app.StatusBarManager; 23 import android.content.Context; 24 import android.content.res.Configuration; 25 import android.content.res.TypedArray; 26 import android.graphics.Canvas; 27 import android.graphics.Paint; 28 import android.graphics.PorterDuff; 29 import android.graphics.PorterDuffXfermode; 30 import android.graphics.Rect; 31 import android.graphics.drawable.Drawable; 32 import android.media.session.MediaSessionLegacyHelper; 33 import android.net.Uri; 34 import android.os.Bundle; 35 import android.os.IBinder; 36 import android.util.AttributeSet; 37 import android.view.ActionMode; 38 import android.view.InputQueue; 39 import android.view.KeyEvent; 40 import android.view.LayoutInflater; 41 import android.view.Menu; 42 import android.view.MenuItem; 43 import android.view.MotionEvent; 44 import android.view.SurfaceHolder; 45 import android.view.View; 46 import android.view.ViewGroup; 47 import android.view.ViewTreeObserver; 48 import android.view.Window; 49 import android.view.WindowManager; 50 import android.view.WindowManagerGlobal; 51 import android.widget.FrameLayout; 52 53 import com.android.internal.view.FloatingActionMode; 54 import com.android.internal.widget.FloatingToolbar; 55 import com.android.systemui.R; 56 import com.android.systemui.classifier.FalsingManager; 57 import com.android.systemui.statusbar.BaseStatusBar; 58 import com.android.systemui.statusbar.DragDownHelper; 59 import com.android.systemui.statusbar.StatusBarState; 60 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; 61 62 63 public class StatusBarWindowView extends FrameLayout { 64 public static final String TAG = "StatusBarWindowView"; 65 public static final boolean DEBUG = BaseStatusBar.DEBUG; 66 67 private DragDownHelper mDragDownHelper; 68 private NotificationStackScrollLayout mStackScrollLayout; 69 private NotificationPanelView mNotificationPanel; 70 private View mBrightnessMirror; 71 72 private int mRightInset = 0; 73 74 private PhoneStatusBar mService; 75 private final Paint mTransparentSrcPaint = new Paint(); 76 private FalsingManager mFalsingManager; 77 78 // Implements the floating action mode for TextView's Cut/Copy/Past menu. Normally provided by 79 // DecorView, but since this is a special window we have to roll our own. 80 private View mFloatingActionModeOriginatingView; 81 private ActionMode mFloatingActionMode; 82 private FloatingToolbar mFloatingToolbar; 83 private ViewTreeObserver.OnPreDrawListener mFloatingToolbarPreDrawListener; 84 StatusBarWindowView(Context context, AttributeSet attrs)85 public StatusBarWindowView(Context context, AttributeSet attrs) { 86 super(context, attrs); 87 setMotionEventSplittingEnabled(false); 88 mTransparentSrcPaint.setColor(0); 89 mTransparentSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); 90 mFalsingManager = FalsingManager.getInstance(context); 91 } 92 93 @Override fitSystemWindows(Rect insets)94 protected boolean fitSystemWindows(Rect insets) { 95 if (getFitsSystemWindows()) { 96 boolean paddingChanged = insets.left != getPaddingLeft() 97 || insets.top != getPaddingTop() 98 || insets.bottom != getPaddingBottom(); 99 100 // Super-special right inset handling, because scrims and backdrop need to ignore it. 101 if (insets.right != mRightInset) { 102 mRightInset = insets.right; 103 applyMargins(); 104 } 105 // Drop top inset, apply left inset and pass through bottom inset. 106 if (paddingChanged) { 107 setPadding(insets.left, 0, 0, 0); 108 } 109 insets.left = 0; 110 insets.top = 0; 111 insets.right = 0; 112 } else { 113 if (mRightInset != 0) { 114 mRightInset = 0; 115 applyMargins(); 116 } 117 boolean changed = getPaddingLeft() != 0 118 || getPaddingRight() != 0 119 || getPaddingTop() != 0 120 || getPaddingBottom() != 0; 121 if (changed) { 122 setPadding(0, 0, 0, 0); 123 } 124 insets.top = 0; 125 } 126 return false; 127 } 128 applyMargins()129 private void applyMargins() { 130 final int N = getChildCount(); 131 for (int i = 0; i < N; i++) { 132 View child = getChildAt(i); 133 if (child.getLayoutParams() instanceof LayoutParams) { 134 LayoutParams lp = (LayoutParams) child.getLayoutParams(); 135 if (!lp.ignoreRightInset && lp.rightMargin != mRightInset) { 136 lp.rightMargin = mRightInset; 137 child.requestLayout(); 138 } 139 } 140 } 141 } 142 143 @Override generateLayoutParams(AttributeSet attrs)144 public FrameLayout.LayoutParams generateLayoutParams(AttributeSet attrs) { 145 return new LayoutParams(getContext(), attrs); 146 } 147 148 @Override generateDefaultLayoutParams()149 protected FrameLayout.LayoutParams generateDefaultLayoutParams() { 150 return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 151 } 152 153 @Override onFinishInflate()154 protected void onFinishInflate() { 155 super.onFinishInflate(); 156 mStackScrollLayout = (NotificationStackScrollLayout) findViewById( 157 R.id.notification_stack_scroller); 158 mNotificationPanel = (NotificationPanelView) findViewById(R.id.notification_panel); 159 mBrightnessMirror = findViewById(R.id.brightness_mirror); 160 } 161 setService(PhoneStatusBar service)162 public void setService(PhoneStatusBar service) { 163 mService = service; 164 mDragDownHelper = new DragDownHelper(getContext(), this, mStackScrollLayout, mService); 165 } 166 167 @Override onAttachedToWindow()168 protected void onAttachedToWindow () { 169 super.onAttachedToWindow(); 170 171 // We need to ensure that our window doesn't suffer from overdraw which would normally 172 // occur if our window is translucent. Since we are drawing the whole window anyway with 173 // the scrim, we don't need the window to be cleared in the beginning. 174 if (mService.isScrimSrcModeEnabled()) { 175 IBinder windowToken = getWindowToken(); 176 WindowManager.LayoutParams lp = (WindowManager.LayoutParams) getLayoutParams(); 177 lp.token = windowToken; 178 setLayoutParams(lp); 179 WindowManagerGlobal.getInstance().changeCanvasOpacity(windowToken, true); 180 setWillNotDraw(false); 181 } else { 182 setWillNotDraw(!DEBUG); 183 } 184 } 185 186 @Override dispatchKeyEvent(KeyEvent event)187 public boolean dispatchKeyEvent(KeyEvent event) { 188 boolean down = event.getAction() == KeyEvent.ACTION_DOWN; 189 switch (event.getKeyCode()) { 190 case KeyEvent.KEYCODE_BACK: 191 if (!down) { 192 mService.onBackPressed(); 193 } 194 return true; 195 case KeyEvent.KEYCODE_MENU: 196 if (!down) { 197 return mService.onMenuPressed(); 198 } 199 case KeyEvent.KEYCODE_SPACE: 200 if (!down) { 201 return mService.onSpacePressed(); 202 } 203 break; 204 case KeyEvent.KEYCODE_VOLUME_DOWN: 205 case KeyEvent.KEYCODE_VOLUME_UP: 206 if (mService.isDozing()) { 207 MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(event, true); 208 return true; 209 } 210 break; 211 } 212 if (mService.interceptMediaKey(event)) { 213 return true; 214 } 215 return super.dispatchKeyEvent(event); 216 } 217 218 @Override dispatchTouchEvent(MotionEvent ev)219 public boolean dispatchTouchEvent(MotionEvent ev) { 220 mFalsingManager.onTouchEvent(ev, getWidth(), getHeight()); 221 if (mBrightnessMirror != null && mBrightnessMirror.getVisibility() == VISIBLE) { 222 // Disallow new pointers while the brightness mirror is visible. This is so that you 223 // can't touch anything other than the brightness slider while the mirror is showing 224 // and the rest of the panel is transparent. 225 if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) { 226 return false; 227 } 228 } 229 if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) { 230 mStackScrollLayout.closeControlsIfOutsideTouch(ev); 231 } 232 233 return super.dispatchTouchEvent(ev); 234 } 235 236 @Override onInterceptTouchEvent(MotionEvent ev)237 public boolean onInterceptTouchEvent(MotionEvent ev) { 238 boolean intercept = false; 239 if (mNotificationPanel.isFullyExpanded() 240 && mStackScrollLayout.getVisibility() == View.VISIBLE 241 && mService.getBarState() == StatusBarState.KEYGUARD 242 && !mService.isBouncerShowing()) { 243 intercept = mDragDownHelper.onInterceptTouchEvent(ev); 244 // wake up on a touch down event, if dozing 245 if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) { 246 mService.wakeUpIfDozing(ev.getEventTime(), ev); 247 } 248 } 249 if (!intercept) { 250 super.onInterceptTouchEvent(ev); 251 } 252 if (intercept) { 253 MotionEvent cancellation = MotionEvent.obtain(ev); 254 cancellation.setAction(MotionEvent.ACTION_CANCEL); 255 mStackScrollLayout.onInterceptTouchEvent(cancellation); 256 mNotificationPanel.onInterceptTouchEvent(cancellation); 257 cancellation.recycle(); 258 } 259 return intercept; 260 } 261 262 @Override onTouchEvent(MotionEvent ev)263 public boolean onTouchEvent(MotionEvent ev) { 264 boolean handled = false; 265 if (mService.getBarState() == StatusBarState.KEYGUARD) { 266 handled = mDragDownHelper.onTouchEvent(ev); 267 } 268 if (!handled) { 269 handled = super.onTouchEvent(ev); 270 } 271 final int action = ev.getAction(); 272 if (!handled && (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL)) { 273 mService.setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false); 274 } 275 return handled; 276 } 277 278 @Override onDraw(Canvas canvas)279 public void onDraw(Canvas canvas) { 280 super.onDraw(canvas); 281 if (mService.isScrimSrcModeEnabled()) { 282 // We need to ensure that our window is always drawn fully even when we have paddings, 283 // since we simulate it to be opaque. 284 int paddedBottom = getHeight() - getPaddingBottom(); 285 int paddedRight = getWidth() - getPaddingRight(); 286 if (getPaddingTop() != 0) { 287 canvas.drawRect(0, 0, getWidth(), getPaddingTop(), mTransparentSrcPaint); 288 } 289 if (getPaddingBottom() != 0) { 290 canvas.drawRect(0, paddedBottom, getWidth(), getHeight(), mTransparentSrcPaint); 291 } 292 if (getPaddingLeft() != 0) { 293 canvas.drawRect(0, getPaddingTop(), getPaddingLeft(), paddedBottom, 294 mTransparentSrcPaint); 295 } 296 if (getPaddingRight() != 0) { 297 canvas.drawRect(paddedRight, getPaddingTop(), getWidth(), paddedBottom, 298 mTransparentSrcPaint); 299 } 300 } 301 if (DEBUG) { 302 Paint pt = new Paint(); 303 pt.setColor(0x80FFFF00); 304 pt.setStrokeWidth(12.0f); 305 pt.setStyle(Paint.Style.STROKE); 306 canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), pt); 307 } 308 } 309 cancelExpandHelper()310 public void cancelExpandHelper() { 311 if (mStackScrollLayout != null) { 312 mStackScrollLayout.cancelExpandHelper(); 313 } 314 } 315 316 public class LayoutParams extends FrameLayout.LayoutParams { 317 318 public boolean ignoreRightInset; 319 LayoutParams(int width, int height)320 public LayoutParams(int width, int height) { 321 super(width, height); 322 } 323 LayoutParams(Context c, AttributeSet attrs)324 public LayoutParams(Context c, AttributeSet attrs) { 325 super(c, attrs); 326 327 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.StatusBarWindowView_Layout); 328 ignoreRightInset = a.getBoolean( 329 R.styleable.StatusBarWindowView_Layout_ignoreRightInset, false); 330 a.recycle(); 331 } 332 } 333 334 @Override startActionModeForChild(View originalView, ActionMode.Callback callback, int type)335 public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback, 336 int type) { 337 if (type == ActionMode.TYPE_FLOATING) { 338 return startActionMode(originalView, callback, type); 339 } 340 return super.startActionModeForChild(originalView, callback, type); 341 } 342 createFloatingActionMode( View originatingView, ActionMode.Callback2 callback)343 private ActionMode createFloatingActionMode( 344 View originatingView, ActionMode.Callback2 callback) { 345 if (mFloatingActionMode != null) { 346 mFloatingActionMode.finish(); 347 } 348 cleanupFloatingActionModeViews(); 349 final FloatingActionMode mode = 350 new FloatingActionMode(mContext, callback, originatingView); 351 mFloatingActionModeOriginatingView = originatingView; 352 mFloatingToolbarPreDrawListener = 353 new ViewTreeObserver.OnPreDrawListener() { 354 @Override 355 public boolean onPreDraw() { 356 mode.updateViewLocationInWindow(); 357 return true; 358 } 359 }; 360 return mode; 361 } 362 setHandledFloatingActionMode(ActionMode mode)363 private void setHandledFloatingActionMode(ActionMode mode) { 364 mFloatingActionMode = mode; 365 mFloatingToolbar = new FloatingToolbar(mContext, mFakeWindow); 366 ((FloatingActionMode) mFloatingActionMode).setFloatingToolbar(mFloatingToolbar); 367 mFloatingActionMode.invalidate(); // Will show the floating toolbar if necessary. 368 mFloatingActionModeOriginatingView.getViewTreeObserver() 369 .addOnPreDrawListener(mFloatingToolbarPreDrawListener); 370 } 371 cleanupFloatingActionModeViews()372 private void cleanupFloatingActionModeViews() { 373 if (mFloatingToolbar != null) { 374 mFloatingToolbar.dismiss(); 375 mFloatingToolbar = null; 376 } 377 if (mFloatingActionModeOriginatingView != null) { 378 if (mFloatingToolbarPreDrawListener != null) { 379 mFloatingActionModeOriginatingView.getViewTreeObserver() 380 .removeOnPreDrawListener(mFloatingToolbarPreDrawListener); 381 mFloatingToolbarPreDrawListener = null; 382 } 383 mFloatingActionModeOriginatingView = null; 384 } 385 } 386 startActionMode( View originatingView, ActionMode.Callback callback, int type)387 private ActionMode startActionMode( 388 View originatingView, ActionMode.Callback callback, int type) { 389 ActionMode.Callback2 wrappedCallback = new ActionModeCallback2Wrapper(callback); 390 ActionMode mode = createFloatingActionMode(originatingView, wrappedCallback); 391 if (mode != null && wrappedCallback.onCreateActionMode(mode, mode.getMenu())) { 392 setHandledFloatingActionMode(mode); 393 } else { 394 mode = null; 395 } 396 return mode; 397 } 398 399 private class ActionModeCallback2Wrapper extends ActionMode.Callback2 { 400 private final ActionMode.Callback mWrapped; 401 ActionModeCallback2Wrapper(ActionMode.Callback wrapped)402 public ActionModeCallback2Wrapper(ActionMode.Callback wrapped) { 403 mWrapped = wrapped; 404 } 405 onCreateActionMode(ActionMode mode, Menu menu)406 public boolean onCreateActionMode(ActionMode mode, Menu menu) { 407 return mWrapped.onCreateActionMode(mode, menu); 408 } 409 onPrepareActionMode(ActionMode mode, Menu menu)410 public boolean onPrepareActionMode(ActionMode mode, Menu menu) { 411 requestFitSystemWindows(); 412 return mWrapped.onPrepareActionMode(mode, menu); 413 } 414 onActionItemClicked(ActionMode mode, MenuItem item)415 public boolean onActionItemClicked(ActionMode mode, MenuItem item) { 416 return mWrapped.onActionItemClicked(mode, item); 417 } 418 onDestroyActionMode(ActionMode mode)419 public void onDestroyActionMode(ActionMode mode) { 420 mWrapped.onDestroyActionMode(mode); 421 if (mode == mFloatingActionMode) { 422 cleanupFloatingActionModeViews(); 423 mFloatingActionMode = null; 424 } 425 requestFitSystemWindows(); 426 } 427 428 @Override onGetContentRect(ActionMode mode, View view, Rect outRect)429 public void onGetContentRect(ActionMode mode, View view, Rect outRect) { 430 if (mWrapped instanceof ActionMode.Callback2) { 431 ((ActionMode.Callback2) mWrapped).onGetContentRect(mode, view, outRect); 432 } else { 433 super.onGetContentRect(mode, view, outRect); 434 } 435 } 436 } 437 438 /** 439 * Minimal window to satisfy FloatingToolbar. 440 */ 441 private Window mFakeWindow = new Window(mContext) { 442 @Override 443 public void takeSurface(SurfaceHolder.Callback2 callback) { 444 } 445 446 @Override 447 public void takeInputQueue(InputQueue.Callback callback) { 448 } 449 450 @Override 451 public boolean isFloating() { 452 return false; 453 } 454 455 @Override 456 public void alwaysReadCloseOnTouchAttr() { 457 } 458 459 @Override 460 public void setContentView(@LayoutRes int layoutResID) { 461 } 462 463 @Override 464 public void setContentView(View view) { 465 } 466 467 @Override 468 public void setContentView(View view, ViewGroup.LayoutParams params) { 469 } 470 471 @Override 472 public void addContentView(View view, ViewGroup.LayoutParams params) { 473 } 474 475 @Override 476 public void clearContentView() { 477 } 478 479 @Override 480 public View getCurrentFocus() { 481 return null; 482 } 483 484 @Override 485 public LayoutInflater getLayoutInflater() { 486 return null; 487 } 488 489 @Override 490 public void setTitle(CharSequence title) { 491 } 492 493 @Override 494 public void setTitleColor(@ColorInt int textColor) { 495 } 496 497 @Override 498 public void openPanel(int featureId, KeyEvent event) { 499 } 500 501 @Override 502 public void closePanel(int featureId) { 503 } 504 505 @Override 506 public void togglePanel(int featureId, KeyEvent event) { 507 } 508 509 @Override 510 public void invalidatePanelMenu(int featureId) { 511 } 512 513 @Override 514 public boolean performPanelShortcut(int featureId, int keyCode, KeyEvent event, int flags) { 515 return false; 516 } 517 518 @Override 519 public boolean performPanelIdentifierAction(int featureId, int id, int flags) { 520 return false; 521 } 522 523 @Override 524 public void closeAllPanels() { 525 } 526 527 @Override 528 public boolean performContextMenuIdentifierAction(int id, int flags) { 529 return false; 530 } 531 532 @Override 533 public void onConfigurationChanged(Configuration newConfig) { 534 } 535 536 @Override 537 public void setBackgroundDrawable(Drawable drawable) { 538 } 539 540 @Override 541 public void setFeatureDrawableResource(int featureId, @DrawableRes int resId) { 542 } 543 544 @Override 545 public void setFeatureDrawableUri(int featureId, Uri uri) { 546 } 547 548 @Override 549 public void setFeatureDrawable(int featureId, Drawable drawable) { 550 } 551 552 @Override 553 public void setFeatureDrawableAlpha(int featureId, int alpha) { 554 } 555 556 @Override 557 public void setFeatureInt(int featureId, int value) { 558 } 559 560 @Override 561 public void takeKeyEvents(boolean get) { 562 } 563 564 @Override 565 public boolean superDispatchKeyEvent(KeyEvent event) { 566 return false; 567 } 568 569 @Override 570 public boolean superDispatchKeyShortcutEvent(KeyEvent event) { 571 return false; 572 } 573 574 @Override 575 public boolean superDispatchTouchEvent(MotionEvent event) { 576 return false; 577 } 578 579 @Override 580 public boolean superDispatchTrackballEvent(MotionEvent event) { 581 return false; 582 } 583 584 @Override 585 public boolean superDispatchGenericMotionEvent(MotionEvent event) { 586 return false; 587 } 588 589 @Override 590 public View getDecorView() { 591 return StatusBarWindowView.this; 592 } 593 594 @Override 595 public View peekDecorView() { 596 return null; 597 } 598 599 @Override 600 public Bundle saveHierarchyState() { 601 return null; 602 } 603 604 @Override 605 public void restoreHierarchyState(Bundle savedInstanceState) { 606 } 607 608 @Override 609 protected void onActive() { 610 } 611 612 @Override 613 public void setChildDrawable(int featureId, Drawable drawable) { 614 } 615 616 @Override 617 public void setChildInt(int featureId, int value) { 618 } 619 620 @Override 621 public boolean isShortcutKey(int keyCode, KeyEvent event) { 622 return false; 623 } 624 625 @Override 626 public void setVolumeControlStream(int streamType) { 627 } 628 629 @Override 630 public int getVolumeControlStream() { 631 return 0; 632 } 633 634 @Override 635 public int getStatusBarColor() { 636 return 0; 637 } 638 639 @Override 640 public void setStatusBarColor(@ColorInt int color) { 641 } 642 643 @Override 644 public int getNavigationBarColor() { 645 return 0; 646 } 647 648 @Override 649 public void setNavigationBarColor(@ColorInt int color) { 650 } 651 652 @Override 653 public void setDecorCaptionShade(int decorCaptionShade) { 654 } 655 656 @Override 657 public void setResizingCaptionDrawable(Drawable drawable) { 658 } 659 660 @Override 661 public void onMultiWindowModeChanged() { 662 } 663 664 @Override 665 public void reportActivityRelaunched() { 666 } 667 }; 668 669 } 670 671