PanelView.java revision 2580a976ec93a01ed00fae51364ad872bc591d95
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 17package com.android.systemui.statusbar.phone; 18 19import android.animation.Animator; 20import android.animation.AnimatorListenerAdapter; 21import android.animation.ObjectAnimator; 22import android.animation.ValueAnimator; 23import android.content.Context; 24import android.content.res.Configuration; 25import android.content.res.Resources; 26import android.util.AttributeSet; 27import android.util.Log; 28import android.view.MotionEvent; 29import android.view.ViewConfiguration; 30import android.view.ViewTreeObserver; 31import android.view.animation.AnimationUtils; 32import android.view.animation.Interpolator; 33import android.widget.FrameLayout; 34 35import com.android.systemui.R; 36import com.android.systemui.statusbar.FlingAnimationUtils; 37import com.android.systemui.statusbar.StatusBarState; 38 39import java.io.FileDescriptor; 40import java.io.PrintWriter; 41 42public abstract class PanelView extends FrameLayout { 43 public static final boolean DEBUG = PanelBar.DEBUG; 44 public static final String TAG = PanelView.class.getSimpleName(); 45 46 private final void logf(String fmt, Object... args) { 47 Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args)); 48 } 49 50 protected PhoneStatusBar mStatusBar; 51 private float mPeekHeight; 52 private float mHintDistance; 53 private int mEdgeTapAreaWidth; 54 private float mInitialOffsetOnTouch; 55 private float mExpandedFraction = 0; 56 protected float mExpandedHeight = 0; 57 private boolean mJustPeeked; 58 private boolean mClosing; 59 protected boolean mTracking; 60 private boolean mTouchSlopExceeded; 61 private int mTrackingPointer; 62 protected int mTouchSlop; 63 protected boolean mHintAnimationRunning; 64 private boolean mOverExpandedBeforeFling; 65 private float mOriginalIndicationY; 66 67 private ValueAnimator mHeightAnimator; 68 private ObjectAnimator mPeekAnimator; 69 private VelocityTrackerInterface mVelocityTracker; 70 private FlingAnimationUtils mFlingAnimationUtils; 71 72 /** 73 * Whether an instant expand request is currently pending and we are just waiting for layout. 74 */ 75 private boolean mInstantExpanding; 76 77 PanelBar mBar; 78 79 protected int mMaxPanelHeight = -1; 80 private String mViewName; 81 private float mInitialTouchY; 82 private float mInitialTouchX; 83 84 private Interpolator mLinearOutSlowInInterpolator; 85 private Interpolator mBounceInterpolator; 86 protected KeyguardBottomAreaView mKeyguardBottomArea; 87 88 protected void onExpandingFinished() { 89 mBar.onExpandingFinished(); 90 } 91 92 protected void onExpandingStarted() { 93 } 94 95 private void runPeekAnimation() { 96 mPeekHeight = getPeekHeight(); 97 if (DEBUG) logf("peek to height=%.1f", mPeekHeight); 98 if (mHeightAnimator != null) { 99 return; 100 } 101 mPeekAnimator = ObjectAnimator.ofFloat(this, 102 "expandedHeight", mPeekHeight) 103 .setDuration(250); 104 mPeekAnimator.start(); 105 } 106 107 public PanelView(Context context, AttributeSet attrs) { 108 super(context, attrs); 109 mFlingAnimationUtils = new FlingAnimationUtils(context, 0.6f); 110 mLinearOutSlowInInterpolator = 111 AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in); 112 mBounceInterpolator = new BounceInterpolator(); 113 } 114 115 protected void loadDimens() { 116 final Resources res = getContext().getResources(); 117 final ViewConfiguration configuration = ViewConfiguration.get(getContext()); 118 mTouchSlop = configuration.getScaledTouchSlop(); 119 mHintDistance = res.getDimension(R.dimen.hint_move_distance); 120 mEdgeTapAreaWidth = res.getDimensionPixelSize(R.dimen.edge_tap_area_width); 121 } 122 123 private void trackMovement(MotionEvent event) { 124 // Add movement to velocity tracker using raw screen X and Y coordinates instead 125 // of window coordinates because the window frame may be moving at the same time. 126 float deltaX = event.getRawX() - event.getX(); 127 float deltaY = event.getRawY() - event.getY(); 128 event.offsetLocation(deltaX, deltaY); 129 if (mVelocityTracker != null) mVelocityTracker.addMovement(event); 130 event.offsetLocation(-deltaX, -deltaY); 131 } 132 133 @Override 134 public boolean onTouchEvent(MotionEvent event) { 135 if (mInstantExpanding) { 136 return false; 137 } 138 139 /* 140 * We capture touch events here and update the expand height here in case according to 141 * the users fingers. This also handles multi-touch. 142 * 143 * If the user just clicks shortly, we give him a quick peek of the shade. 144 * 145 * Flinging is also enabled in order to open or close the shade. 146 */ 147 148 int pointerIndex = event.findPointerIndex(mTrackingPointer); 149 if (pointerIndex < 0) { 150 pointerIndex = 0; 151 mTrackingPointer = event.getPointerId(pointerIndex); 152 } 153 final float y = event.getY(pointerIndex); 154 final float x = event.getX(pointerIndex); 155 156 boolean waitForTouchSlop = hasConflictingGestures(); 157 158 switch (event.getActionMasked()) { 159 case MotionEvent.ACTION_DOWN: 160 mInitialTouchY = y; 161 mInitialTouchX = x; 162 mInitialOffsetOnTouch = mExpandedHeight; 163 mTouchSlopExceeded = false; 164 if (mVelocityTracker == null) { 165 initVelocityTracker(); 166 } 167 trackMovement(event); 168 if (!waitForTouchSlop || (mHeightAnimator != null && !mHintAnimationRunning)) { 169 if (mHeightAnimator != null) { 170 mHeightAnimator.cancel(); // end any outstanding animations 171 } 172 mTouchSlopExceeded = (mHeightAnimator != null && !mHintAnimationRunning); 173 onTrackingStarted(); 174 } 175 if (mExpandedHeight == 0) { 176 mJustPeeked = true; 177 runPeekAnimation(); 178 } 179 break; 180 181 case MotionEvent.ACTION_POINTER_UP: 182 final int upPointer = event.getPointerId(event.getActionIndex()); 183 if (mTrackingPointer == upPointer) { 184 // gesture is ongoing, find a new pointer to track 185 final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1; 186 final float newY = event.getY(newIndex); 187 final float newX = event.getX(newIndex); 188 mTrackingPointer = event.getPointerId(newIndex); 189 mInitialOffsetOnTouch = mExpandedHeight; 190 mInitialTouchY = newY; 191 mInitialTouchX = newX; 192 } 193 break; 194 195 case MotionEvent.ACTION_MOVE: 196 float h = y - mInitialTouchY; 197 if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX)) { 198 mTouchSlopExceeded = true; 199 if (waitForTouchSlop && !mTracking) { 200 mInitialOffsetOnTouch = mExpandedHeight; 201 mInitialTouchX = x; 202 mInitialTouchY = y; 203 if (mHeightAnimator != null) { 204 mHeightAnimator.cancel(); // end any outstanding animations 205 } 206 onTrackingStarted(); 207 h = 0; 208 } 209 } 210 final float newHeight = h + mInitialOffsetOnTouch; 211 if (newHeight > mPeekHeight) { 212 if (mPeekAnimator != null && mPeekAnimator.isStarted()) { 213 mPeekAnimator.cancel(); 214 } 215 mJustPeeked = false; 216 } 217 if (!mJustPeeked && (!waitForTouchSlop || mTracking)) { 218 setExpandedHeightInternal(newHeight); 219 mBar.panelExpansionChanged(PanelView.this, mExpandedFraction); 220 } 221 222 trackMovement(event); 223 break; 224 225 case MotionEvent.ACTION_UP: 226 case MotionEvent.ACTION_CANCEL: 227 mTrackingPointer = -1; 228 trackMovement(event); 229 if ((mTracking && mTouchSlopExceeded) 230 || event.getActionMasked() == MotionEvent.ACTION_CANCEL) { 231 float vel = getCurrentVelocity(); 232 boolean expand = flingExpands(vel); 233 onTrackingStopped(expand); 234 fling(vel, expand); 235 } else { 236 boolean expands = onEmptySpaceClick(mInitialTouchX); 237 onTrackingStopped(expands); 238 } 239 if (mVelocityTracker != null) { 240 mVelocityTracker.recycle(); 241 mVelocityTracker = null; 242 } 243 break; 244 } 245 return !waitForTouchSlop || mTracking; 246 } 247 248 protected abstract boolean hasConflictingGestures(); 249 250 protected void onTrackingStopped(boolean expand) { 251 mTracking = false; 252 mBar.onTrackingStopped(PanelView.this, expand); 253 } 254 255 protected void onTrackingStarted() { 256 mTracking = true; 257 mBar.onTrackingStarted(PanelView.this); 258 onExpandingStarted(); 259 } 260 261 private float getCurrentVelocity() { 262 263 // the velocitytracker might be null if we got a bad input stream 264 if (mVelocityTracker == null) { 265 return 0; 266 } 267 mVelocityTracker.computeCurrentVelocity(1000); 268 return mVelocityTracker.getYVelocity(); 269 } 270 271 @Override 272 public boolean onInterceptTouchEvent(MotionEvent event) { 273 if (mInstantExpanding) { 274 return false; 275 } 276 277 /* 278 * If the user drags anywhere inside the panel we intercept it if he moves his finger 279 * upwards. This allows closing the shade from anywhere inside the panel. 280 * 281 * We only do this if the current content is scrolled to the bottom, 282 * i.e isScrolledToBottom() is true and therefore there is no conflicting scrolling gesture 283 * possible. 284 */ 285 int pointerIndex = event.findPointerIndex(mTrackingPointer); 286 if (pointerIndex < 0) { 287 pointerIndex = 0; 288 mTrackingPointer = event.getPointerId(pointerIndex); 289 } 290 final float x = event.getX(pointerIndex); 291 final float y = event.getY(pointerIndex); 292 boolean scrolledToBottom = isScrolledToBottom(); 293 294 switch (event.getActionMasked()) { 295 case MotionEvent.ACTION_DOWN: 296 if (mHeightAnimator != null && !mHintAnimationRunning) { 297 mHeightAnimator.cancel(); // end any outstanding animations 298 mTouchSlopExceeded = true; 299 return true; 300 } 301 mInitialTouchY = y; 302 mInitialTouchX = x; 303 mTouchSlopExceeded = false; 304 initVelocityTracker(); 305 trackMovement(event); 306 break; 307 case MotionEvent.ACTION_POINTER_UP: 308 final int upPointer = event.getPointerId(event.getActionIndex()); 309 if (mTrackingPointer == upPointer) { 310 // gesture is ongoing, find a new pointer to track 311 final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1; 312 mTrackingPointer = event.getPointerId(newIndex); 313 mInitialTouchX = event.getX(newIndex); 314 mInitialTouchY = event.getY(newIndex); 315 } 316 break; 317 318 case MotionEvent.ACTION_MOVE: 319 final float h = y - mInitialTouchY; 320 trackMovement(event); 321 if (scrolledToBottom) { 322 if (h < -mTouchSlop && h < -Math.abs(x - mInitialTouchX)) { 323 if (mHeightAnimator != null) { 324 mHeightAnimator.cancel(); 325 } 326 mInitialOffsetOnTouch = mExpandedHeight; 327 mInitialTouchY = y; 328 mInitialTouchX = x; 329 mTracking = true; 330 mTouchSlopExceeded = true; 331 onTrackingStarted(); 332 return true; 333 } 334 } 335 break; 336 } 337 return false; 338 } 339 340 private void initVelocityTracker() { 341 if (mVelocityTracker != null) { 342 mVelocityTracker.recycle(); 343 } 344 mVelocityTracker = VelocityTrackerFactory.obtain(getContext()); 345 } 346 347 protected boolean isScrolledToBottom() { 348 return true; 349 } 350 351 protected float getContentHeight() { 352 return mExpandedHeight; 353 } 354 355 @Override 356 protected void onFinishInflate() { 357 super.onFinishInflate(); 358 loadDimens(); 359 } 360 361 @Override 362 protected void onConfigurationChanged(Configuration newConfig) { 363 super.onConfigurationChanged(newConfig); 364 loadDimens(); 365 mMaxPanelHeight = -1; 366 } 367 368 /** 369 * @param vel the current velocity of the motion 370 * @return whether a fling should expands the panel; contracts otherwise 371 */ 372 private boolean flingExpands(float vel) { 373 if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) { 374 return getExpandedFraction() > 0.5f; 375 } else { 376 return vel > 0; 377 } 378 } 379 380 protected void fling(float vel, boolean expand) { 381 cancelPeek(); 382 float target = expand ? getMaxPanelHeight() : 0.0f; 383 if (target == mExpandedHeight || getOverExpansionAmount() > 0f && expand) { 384 onExpandingFinished(); 385 mBar.panelExpansionChanged(this, mExpandedFraction); 386 return; 387 } 388 mOverExpandedBeforeFling = getOverExpansionAmount() > 0f; 389 ValueAnimator animator = createHeightAnimator(target); 390 if (expand) { 391 mFlingAnimationUtils.apply(animator, mExpandedHeight, target, vel, getHeight()); 392 } else { 393 mFlingAnimationUtils.applyDismissing(animator, mExpandedHeight, target, vel, 394 getHeight()); 395 396 // Make it shorter if we run a canned animation 397 if (vel == 0) { 398 animator.setDuration((long) (animator.getDuration() / 1.75f)); 399 } 400 } 401 animator.addListener(new AnimatorListenerAdapter() { 402 @Override 403 public void onAnimationEnd(Animator animation) { 404 mHeightAnimator = null; 405 onExpandingFinished(); 406 } 407 }); 408 mHeightAnimator = animator; 409 animator.start(); 410 } 411 412 @Override 413 protected void onAttachedToWindow() { 414 super.onAttachedToWindow(); 415 mViewName = getResources().getResourceName(getId()); 416 } 417 418 public String getName() { 419 return mViewName; 420 } 421 422 @Override 423 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 424 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 425 426 if (DEBUG) logf("onMeasure(%d, %d) -> (%d, %d)", 427 widthMeasureSpec, heightMeasureSpec, getMeasuredWidth(), getMeasuredHeight()); 428 429 // Did one of our children change size? 430 int newHeight = getMeasuredHeight(); 431 if (newHeight > mMaxPanelHeight) { 432 // we only adapt the max height if it's bigger 433 mMaxPanelHeight = newHeight; 434 // If the user isn't actively poking us, let's rubberband to the content 435 if (!mTracking && mHeightAnimator == null 436 && mExpandedHeight > 0 && mExpandedHeight != mMaxPanelHeight 437 && mMaxPanelHeight > 0) { 438 mExpandedHeight = mMaxPanelHeight; 439 } 440 } 441 } 442 443 public void setExpandedHeight(float height) { 444 if (DEBUG) logf("setExpandedHeight(%.1f)", height); 445 setExpandedHeightInternal(height + getOverExpansionPixels()); 446 mBar.panelExpansionChanged(PanelView.this, mExpandedFraction); 447 } 448 449 @Override 450 protected void onLayout (boolean changed, int left, int top, int right, int bottom) { 451 if (DEBUG) logf("onLayout: changed=%s, bottom=%d eh=%d fh=%d", changed?"T":"f", bottom, 452 (int)mExpandedHeight, mMaxPanelHeight); 453 super.onLayout(changed, left, top, right, bottom); 454 requestPanelHeightUpdate(); 455 } 456 457 protected void requestPanelHeightUpdate() { 458 float currentMaxPanelHeight = getMaxPanelHeight(); 459 460 // If the user isn't actively poking us, let's update the height 461 if (!mTracking && mHeightAnimator == null 462 && mExpandedHeight > 0 && currentMaxPanelHeight != mExpandedHeight) { 463 setExpandedHeight(currentMaxPanelHeight); 464 } 465 } 466 467 public void setExpandedHeightInternal(float h) { 468 float fhWithoutOverExpansion = getMaxPanelHeight() - getOverExpansionAmount(); 469 if (mHeightAnimator == null) { 470 float overExpansionPixels = Math.max(0, h - fhWithoutOverExpansion); 471 if (getOverExpansionPixels() != overExpansionPixels && mTracking) { 472 setOverExpansion(overExpansionPixels, true /* isPixels */); 473 } 474 mExpandedHeight = Math.min(h, fhWithoutOverExpansion) + getOverExpansionAmount(); 475 } else { 476 mExpandedHeight = h; 477 if (mOverExpandedBeforeFling) { 478 setOverExpansion(Math.max(0, h - fhWithoutOverExpansion), false /* isPixels */); 479 } 480 } 481 482 onHeightUpdated(mExpandedHeight); 483 mExpandedFraction = Math.min(1f, fhWithoutOverExpansion == 0 484 ? 0 485 : mExpandedHeight / fhWithoutOverExpansion); 486 } 487 488 protected abstract void setOverExpansion(float overExpansion, boolean isPixels); 489 490 protected abstract void onHeightUpdated(float expandedHeight); 491 492 protected abstract float getOverExpansionAmount(); 493 494 protected abstract float getOverExpansionPixels(); 495 496 /** 497 * This returns the maximum height of the panel. Children should override this if their 498 * desired height is not the full height. 499 * 500 * @return the default implementation simply returns the maximum height. 501 */ 502 protected int getMaxPanelHeight() { 503 mMaxPanelHeight = Math.max(mMaxPanelHeight, getHeight()); 504 return mMaxPanelHeight; 505 } 506 507 public void setExpandedFraction(float frac) { 508 setExpandedHeight(getMaxPanelHeight() * frac); 509 } 510 511 public float getExpandedHeight() { 512 return mExpandedHeight; 513 } 514 515 public float getExpandedFraction() { 516 return mExpandedFraction; 517 } 518 519 public boolean isFullyExpanded() { 520 return mExpandedHeight >= getMaxPanelHeight(); 521 } 522 523 public boolean isFullyCollapsed() { 524 return mExpandedHeight <= 0; 525 } 526 527 public boolean isCollapsing() { 528 return mClosing; 529 } 530 531 public boolean isTracking() { 532 return mTracking; 533 } 534 535 public void setBar(PanelBar panelBar) { 536 mBar = panelBar; 537 } 538 539 public void collapse() { 540 // TODO: abort animation or ongoing touch 541 if (DEBUG) logf("collapse: " + this); 542 if (!isFullyCollapsed()) { 543 if (mHeightAnimator != null) { 544 mHeightAnimator.cancel(); 545 } 546 mClosing = true; 547 onExpandingStarted(); 548 fling(0, false /* expand */); 549 } 550 } 551 552 public void expand() { 553 if (DEBUG) logf("expand: " + this); 554 if (isFullyCollapsed()) { 555 mBar.startOpeningPanel(this); 556 onExpandingStarted(); 557 fling(0, true /* expand */); 558 } else if (DEBUG) { 559 if (DEBUG) logf("skipping expansion: is expanded"); 560 } 561 } 562 563 public void cancelPeek() { 564 if (mPeekAnimator != null && mPeekAnimator.isStarted()) { 565 mPeekAnimator.cancel(); 566 } 567 } 568 569 public void instantExpand() { 570 mInstantExpanding = true; 571 abortAnimations(); 572 if (mTracking) { 573 onTrackingStopped(true /* expands */); // The panel is expanded after this call. 574 onExpandingFinished(); 575 } 576 setVisibility(VISIBLE); 577 578 // Wait for window manager to pickup the change, so we know the maximum height of the panel 579 // then. 580 getViewTreeObserver().addOnGlobalLayoutListener( 581 new ViewTreeObserver.OnGlobalLayoutListener() { 582 @Override 583 public void onGlobalLayout() { 584 if (mStatusBar.getStatusBarWindow().getHeight() 585 != mStatusBar.getStatusBarHeight()) { 586 getViewTreeObserver().removeOnGlobalLayoutListener(this); 587 setExpandedFraction(1f); 588 mInstantExpanding = false; 589 } 590 } 591 }); 592 593 // Make sure a layout really happens. 594 requestLayout(); 595 } 596 597 private void abortAnimations() { 598 cancelPeek(); 599 if (mHeightAnimator != null) { 600 mHeightAnimator.cancel(); 601 } 602 } 603 604 protected void startUnlockHintAnimation() { 605 606 // We don't need to hint the user if an animation is already running or the user is changing 607 // the expansion. 608 if (mHeightAnimator != null || mTracking) { 609 return; 610 } 611 cancelPeek(); 612 onExpandingStarted(); 613 startUnlockHintAnimationPhase1(new Runnable() { 614 @Override 615 public void run() { 616 onExpandingFinished(); 617 mStatusBar.onHintFinished(); 618 mHintAnimationRunning = false; 619 } 620 }); 621 mStatusBar.onUnlockHintStarted(); 622 mHintAnimationRunning = true; 623 } 624 625 /** 626 * Phase 1: Move everything upwards. 627 */ 628 private void startUnlockHintAnimationPhase1(final Runnable onAnimationFinished) { 629 float target = Math.max(0, getMaxPanelHeight() - mHintDistance); 630 ValueAnimator animator = createHeightAnimator(target); 631 animator.setDuration(250); 632 animator.setInterpolator(mLinearOutSlowInInterpolator); 633 animator.addListener(new AnimatorListenerAdapter() { 634 private boolean mCancelled; 635 636 @Override 637 public void onAnimationCancel(Animator animation) { 638 mCancelled = true; 639 } 640 641 @Override 642 public void onAnimationEnd(Animator animation) { 643 if (mCancelled) { 644 mHeightAnimator = null; 645 onAnimationFinished.run(); 646 } else { 647 startUnlockHintAnimationPhase2(onAnimationFinished); 648 } 649 } 650 }); 651 animator.start(); 652 mHeightAnimator = animator; 653 mOriginalIndicationY = mKeyguardBottomArea.getIndicationView().getY(); 654 mKeyguardBottomArea.getIndicationView().animate() 655 .y(mOriginalIndicationY - mHintDistance) 656 .setDuration(250) 657 .setInterpolator(mLinearOutSlowInInterpolator) 658 .withEndAction(new Runnable() { 659 @Override 660 public void run() { 661 mKeyguardBottomArea.getIndicationView().animate() 662 .y(mOriginalIndicationY) 663 .setDuration(450) 664 .setInterpolator(mBounceInterpolator) 665 .start(); 666 } 667 }) 668 .start(); 669 } 670 671 /** 672 * Phase 2: Bounce down. 673 */ 674 private void startUnlockHintAnimationPhase2(final Runnable onAnimationFinished) { 675 ValueAnimator animator = createHeightAnimator(getMaxPanelHeight()); 676 animator.setDuration(450); 677 animator.setInterpolator(mBounceInterpolator); 678 animator.addListener(new AnimatorListenerAdapter() { 679 @Override 680 public void onAnimationEnd(Animator animation) { 681 mHeightAnimator = null; 682 onAnimationFinished.run(); 683 } 684 }); 685 animator.start(); 686 mHeightAnimator = animator; 687 } 688 689 private ValueAnimator createHeightAnimator(float targetHeight) { 690 ValueAnimator animator = ValueAnimator.ofFloat(mExpandedHeight, targetHeight); 691 animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 692 @Override 693 public void onAnimationUpdate(ValueAnimator animation) { 694 setExpandedHeightInternal((Float) animation.getAnimatedValue()); 695 mBar.panelExpansionChanged(PanelView.this, mExpandedFraction); 696 } 697 }); 698 return animator; 699 } 700 701 /** 702 * Gets called when the user performs a click anywhere in the empty area of the panel. 703 * 704 * @return whether the panel will be expanded after the action performed by this method 705 */ 706 private boolean onEmptySpaceClick(float x) { 707 if (mHintAnimationRunning) { 708 return true; 709 } 710 if (x < mEdgeTapAreaWidth 711 && mStatusBar.getBarState() == StatusBarState.KEYGUARD) { 712 onEdgeClicked(false /* right */); 713 return true; 714 } else if (x > getWidth() - mEdgeTapAreaWidth 715 && mStatusBar.getBarState() == StatusBarState.KEYGUARD) { 716 onEdgeClicked(true /* right */); 717 return true; 718 } else { 719 return onMiddleClicked(); 720 } 721 } 722 723 private boolean onMiddleClicked() { 724 switch (mStatusBar.getBarState()) { 725 case StatusBarState.KEYGUARD: 726 startUnlockHintAnimation(); 727 return true; 728 case StatusBarState.SHADE_LOCKED: 729 mStatusBar.goToKeyguard(); 730 return true; 731 case StatusBarState.SHADE: 732 collapse(); 733 return false; 734 default: 735 return true; 736 } 737 } 738 739 protected abstract void onEdgeClicked(boolean right); 740 741 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 742 pw.println(String.format("[PanelView(%s): expandedHeight=%f maxPanelHeight=%d closing=%s" 743 + " tracking=%s justPeeked=%s peekAnim=%s%s timeAnim=%s%s" 744 + "]", 745 this.getClass().getSimpleName(), 746 getExpandedHeight(), 747 getMaxPanelHeight(), 748 mClosing?"T":"f", 749 mTracking?"T":"f", 750 mJustPeeked?"T":"f", 751 mPeekAnimator, ((mPeekAnimator!=null && mPeekAnimator.isStarted())?" (started)":""), 752 mHeightAnimator, ((mHeightAnimator !=null && mHeightAnimator.isStarted())?" (started)":"") 753 )); 754 } 755 756 public abstract void resetViews(); 757 758 protected abstract float getPeekHeight(); 759} 760