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 18 package com.android.systemui; 19 20 import android.animation.Animator; 21 import android.animation.AnimatorListenerAdapter; 22 import android.animation.ObjectAnimator; 23 import android.content.Context; 24 import android.util.Log; 25 import android.view.Gravity; 26 import android.view.HapticFeedbackConstants; 27 import android.view.MotionEvent; 28 import android.view.ScaleGestureDetector; 29 import android.view.ScaleGestureDetector.OnScaleGestureListener; 30 import android.view.VelocityTracker; 31 import android.view.View; 32 import android.view.ViewConfiguration; 33 34 import com.android.internal.annotations.VisibleForTesting; 35 import com.android.systemui.statusbar.FlingAnimationUtils; 36 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; 37 import com.android.systemui.statusbar.notification.row.ExpandableView; 38 import com.android.systemui.statusbar.policy.ScrollAdapter; 39 40 public class ExpandHelper implements Gefingerpoken { 41 public interface Callback { getChildAtRawPosition(float x, float y)42 ExpandableView getChildAtRawPosition(float x, float y); getChildAtPosition(float x, float y)43 ExpandableView getChildAtPosition(float x, float y); canChildBeExpanded(View v)44 boolean canChildBeExpanded(View v); setUserExpandedChild(View v, boolean userExpanded)45 void setUserExpandedChild(View v, boolean userExpanded); setUserLockedChild(View v, boolean userLocked)46 void setUserLockedChild(View v, boolean userLocked); expansionStateChanged(boolean isExpanding)47 void expansionStateChanged(boolean isExpanding); getMaxExpandHeight(ExpandableView view)48 int getMaxExpandHeight(ExpandableView view); setExpansionCancelled(View view)49 void setExpansionCancelled(View view); 50 } 51 52 private static final String TAG = "ExpandHelper"; 53 protected static final boolean DEBUG = false; 54 protected static final boolean DEBUG_SCALE = false; 55 private static final float EXPAND_DURATION = 0.3f; 56 57 // Set to false to disable focus-based gestures (spread-finger vertical pull). 58 private static final boolean USE_DRAG = true; 59 // Set to false to disable scale-based gestures (both horizontal and vertical). 60 private static final boolean USE_SPAN = true; 61 // Both gestures types may be active at the same time. 62 // At least one gesture type should be active. 63 // A variant of the screwdriver gesture will emerge from either gesture type. 64 65 // amount of overstretch for maximum brightness expressed in U 66 // 2f: maximum brightness is stretching a 1U to 3U, or a 4U to 6U 67 private static final float STRETCH_INTERVAL = 2f; 68 69 @SuppressWarnings("unused") 70 private Context mContext; 71 72 private boolean mExpanding; 73 private static final int NONE = 0; 74 private static final int BLINDS = 1<<0; 75 private static final int PULL = 1<<1; 76 private static final int STRETCH = 1<<2; 77 private int mExpansionStyle = NONE; 78 private boolean mWatchingForPull; 79 private boolean mHasPopped; 80 private View mEventSource; 81 private float mOldHeight; 82 private float mNaturalHeight; 83 private float mInitialTouchFocusY; 84 private float mInitialTouchX; 85 private float mInitialTouchY; 86 private float mInitialTouchSpan; 87 private float mLastFocusY; 88 private float mLastSpanY; 89 private int mTouchSlop; 90 private float mLastMotionY; 91 private float mPullGestureMinXSpan; 92 private Callback mCallback; 93 private ScaleGestureDetector mSGD; 94 private ViewScaler mScaler; 95 private ObjectAnimator mScaleAnimation; 96 private boolean mEnabled = true; 97 private ExpandableView mResizedView; 98 private float mCurrentHeight; 99 100 private int mSmallSize; 101 private int mLargeSize; 102 private float mMaximumStretch; 103 private boolean mOnlyMovements; 104 105 private int mGravity; 106 107 private ScrollAdapter mScrollAdapter; 108 private FlingAnimationUtils mFlingAnimationUtils; 109 private VelocityTracker mVelocityTracker; 110 111 private OnScaleGestureListener mScaleGestureListener 112 = new ScaleGestureDetector.SimpleOnScaleGestureListener() { 113 @Override 114 public boolean onScaleBegin(ScaleGestureDetector detector) { 115 if (DEBUG_SCALE) Log.v(TAG, "onscalebegin()"); 116 117 if (!mOnlyMovements) { 118 startExpanding(mResizedView, STRETCH); 119 } 120 return mExpanding; 121 } 122 123 @Override 124 public boolean onScale(ScaleGestureDetector detector) { 125 if (DEBUG_SCALE) Log.v(TAG, "onscale() on " + mResizedView); 126 return true; 127 } 128 129 @Override 130 public void onScaleEnd(ScaleGestureDetector detector) { 131 } 132 }; 133 134 @VisibleForTesting getScaleAnimation()135 ObjectAnimator getScaleAnimation() { 136 return mScaleAnimation; 137 } 138 139 private class ViewScaler { 140 ExpandableView mView; 141 ViewScaler()142 public ViewScaler() {} setView(ExpandableView v)143 public void setView(ExpandableView v) { 144 mView = v; 145 } setHeight(float h)146 public void setHeight(float h) { 147 if (DEBUG_SCALE) Log.v(TAG, "SetHeight: setting to " + h); 148 mView.setActualHeight((int) h); 149 mCurrentHeight = h; 150 } getHeight()151 public float getHeight() { 152 return mView.getActualHeight(); 153 } getNaturalHeight()154 public int getNaturalHeight() { 155 return mCallback.getMaxExpandHeight(mView); 156 } 157 } 158 159 /** 160 * Handle expansion gestures to expand and contract children of the callback. 161 * 162 * @param context application context 163 * @param callback the container that holds the items to be manipulated 164 * @param small the smallest allowable size for the manuipulated items. 165 * @param large the largest allowable size for the manuipulated items. 166 */ ExpandHelper(Context context, Callback callback, int small, int large)167 public ExpandHelper(Context context, Callback callback, int small, int large) { 168 mSmallSize = small; 169 mMaximumStretch = mSmallSize * STRETCH_INTERVAL; 170 mLargeSize = large; 171 mContext = context; 172 mCallback = callback; 173 mScaler = new ViewScaler(); 174 mGravity = Gravity.TOP; 175 mScaleAnimation = ObjectAnimator.ofFloat(mScaler, "height", 0f); 176 mPullGestureMinXSpan = mContext.getResources().getDimension(R.dimen.pull_span_min); 177 178 final ViewConfiguration configuration = ViewConfiguration.get(mContext); 179 mTouchSlop = configuration.getScaledTouchSlop(); 180 181 mSGD = new ScaleGestureDetector(context, mScaleGestureListener); 182 mFlingAnimationUtils = new FlingAnimationUtils(context, EXPAND_DURATION); 183 } 184 185 @VisibleForTesting updateExpansion()186 void updateExpansion() { 187 if (DEBUG_SCALE) Log.v(TAG, "updateExpansion()"); 188 // are we scaling or dragging? 189 float span = mSGD.getCurrentSpan() - mInitialTouchSpan; 190 span *= USE_SPAN ? 1f : 0f; 191 float drag = mSGD.getFocusY() - mInitialTouchFocusY; 192 drag *= USE_DRAG ? 1f : 0f; 193 drag *= mGravity == Gravity.BOTTOM ? -1f : 1f; 194 float pull = Math.abs(drag) + Math.abs(span) + 1f; 195 float hand = drag * Math.abs(drag) / pull + span * Math.abs(span) / pull; 196 float target = hand + mOldHeight; 197 float newHeight = clamp(target); 198 mScaler.setHeight(newHeight); 199 mLastFocusY = mSGD.getFocusY(); 200 mLastSpanY = mSGD.getCurrentSpan(); 201 } 202 clamp(float target)203 private float clamp(float target) { 204 float out = target; 205 out = out < mSmallSize ? mSmallSize : out; 206 out = out > mNaturalHeight ? mNaturalHeight : out; 207 return out; 208 } 209 findView(float x, float y)210 private ExpandableView findView(float x, float y) { 211 ExpandableView v; 212 if (mEventSource != null) { 213 int[] location = new int[2]; 214 mEventSource.getLocationOnScreen(location); 215 x += location[0]; 216 y += location[1]; 217 v = mCallback.getChildAtRawPosition(x, y); 218 } else { 219 v = mCallback.getChildAtPosition(x, y); 220 } 221 return v; 222 } 223 isInside(View v, float x, float y)224 private boolean isInside(View v, float x, float y) { 225 if (DEBUG) Log.d(TAG, "isinside (" + x + ", " + y + ")"); 226 227 if (v == null) { 228 if (DEBUG) Log.d(TAG, "isinside null subject"); 229 return false; 230 } 231 if (mEventSource != null) { 232 int[] location = new int[2]; 233 mEventSource.getLocationOnScreen(location); 234 x += location[0]; 235 y += location[1]; 236 if (DEBUG) Log.d(TAG, " to global (" + x + ", " + y + ")"); 237 } 238 int[] location = new int[2]; 239 v.getLocationOnScreen(location); 240 x -= location[0]; 241 y -= location[1]; 242 if (DEBUG) Log.d(TAG, " to local (" + x + ", " + y + ")"); 243 if (DEBUG) Log.d(TAG, " inside (" + v.getWidth() + ", " + v.getHeight() + ")"); 244 boolean inside = (x > 0f && y > 0f && x < v.getWidth() & y < v.getHeight()); 245 return inside; 246 } 247 setEventSource(View eventSource)248 public void setEventSource(View eventSource) { 249 mEventSource = eventSource; 250 } 251 setGravity(int gravity)252 public void setGravity(int gravity) { 253 mGravity = gravity; 254 } 255 setScrollAdapter(ScrollAdapter adapter)256 public void setScrollAdapter(ScrollAdapter adapter) { 257 mScrollAdapter = adapter; 258 } 259 260 @Override onInterceptTouchEvent(MotionEvent ev)261 public boolean onInterceptTouchEvent(MotionEvent ev) { 262 if (!isEnabled()) { 263 return false; 264 } 265 trackVelocity(ev); 266 final int action = ev.getAction(); 267 if (DEBUG_SCALE) Log.d(TAG, "intercept: act=" + MotionEvent.actionToString(action) + 268 " expanding=" + mExpanding + 269 (0 != (mExpansionStyle & BLINDS) ? " (blinds)" : "") + 270 (0 != (mExpansionStyle & PULL) ? " (pull)" : "") + 271 (0 != (mExpansionStyle & STRETCH) ? " (stretch)" : "")); 272 // check for a spread-finger vertical pull gesture 273 mSGD.onTouchEvent(ev); 274 final int x = (int) mSGD.getFocusX(); 275 final int y = (int) mSGD.getFocusY(); 276 277 mInitialTouchFocusY = y; 278 mInitialTouchSpan = mSGD.getCurrentSpan(); 279 mLastFocusY = mInitialTouchFocusY; 280 mLastSpanY = mInitialTouchSpan; 281 if (DEBUG_SCALE) Log.d(TAG, "set initial span: " + mInitialTouchSpan); 282 283 if (mExpanding) { 284 mLastMotionY = ev.getRawY(); 285 maybeRecycleVelocityTracker(ev); 286 return true; 287 } else { 288 if ((action == MotionEvent.ACTION_MOVE) && 0 != (mExpansionStyle & BLINDS)) { 289 // we've begun Venetian blinds style expansion 290 return true; 291 } 292 switch (action & MotionEvent.ACTION_MASK) { 293 case MotionEvent.ACTION_MOVE: { 294 final float xspan = mSGD.getCurrentSpanX(); 295 if (xspan > mPullGestureMinXSpan && 296 xspan > mSGD.getCurrentSpanY() && !mExpanding) { 297 // detect a vertical pulling gesture with fingers somewhat separated 298 if (DEBUG_SCALE) Log.v(TAG, "got pull gesture (xspan=" + xspan + "px)"); 299 startExpanding(mResizedView, PULL); 300 mWatchingForPull = false; 301 } 302 if (mWatchingForPull) { 303 final float yDiff = ev.getRawY() - mInitialTouchY; 304 final float xDiff = ev.getRawX() - mInitialTouchX; 305 if (yDiff > mTouchSlop && yDiff > Math.abs(xDiff)) { 306 if (DEBUG) Log.v(TAG, "got venetian gesture (dy=" + yDiff + "px)"); 307 mWatchingForPull = false; 308 if (mResizedView != null && !isFullyExpanded(mResizedView)) { 309 if (startExpanding(mResizedView, BLINDS)) { 310 mLastMotionY = ev.getRawY(); 311 mInitialTouchY = ev.getRawY(); 312 mHasPopped = false; 313 } 314 } 315 } 316 } 317 break; 318 } 319 320 case MotionEvent.ACTION_DOWN: 321 mWatchingForPull = mScrollAdapter != null && 322 isInside(mScrollAdapter.getHostView(), x, y) 323 && mScrollAdapter.isScrolledToTop(); 324 mResizedView = findView(x, y); 325 if (mResizedView != null && !mCallback.canChildBeExpanded(mResizedView)) { 326 mResizedView = null; 327 mWatchingForPull = false; 328 } 329 mInitialTouchY = ev.getRawY(); 330 mInitialTouchX = ev.getRawX(); 331 break; 332 333 case MotionEvent.ACTION_CANCEL: 334 case MotionEvent.ACTION_UP: 335 if (DEBUG) Log.d(TAG, "up/cancel"); 336 finishExpanding(ev.getActionMasked() == MotionEvent.ACTION_CANCEL /* forceAbort */, 337 getCurrentVelocity()); 338 clearView(); 339 break; 340 } 341 mLastMotionY = ev.getRawY(); 342 maybeRecycleVelocityTracker(ev); 343 return mExpanding; 344 } 345 } 346 trackVelocity(MotionEvent event)347 private void trackVelocity(MotionEvent event) { 348 int action = event.getActionMasked(); 349 switch(action) { 350 case MotionEvent.ACTION_DOWN: 351 if (mVelocityTracker == null) { 352 mVelocityTracker = VelocityTracker.obtain(); 353 } else { 354 mVelocityTracker.clear(); 355 } 356 mVelocityTracker.addMovement(event); 357 break; 358 case MotionEvent.ACTION_MOVE: 359 if (mVelocityTracker == null) { 360 mVelocityTracker = VelocityTracker.obtain(); 361 } 362 mVelocityTracker.addMovement(event); 363 break; 364 default: 365 break; 366 } 367 } 368 maybeRecycleVelocityTracker(MotionEvent event)369 private void maybeRecycleVelocityTracker(MotionEvent event) { 370 if (mVelocityTracker != null && (event.getActionMasked() == MotionEvent.ACTION_CANCEL 371 || event.getActionMasked() == MotionEvent.ACTION_UP)) { 372 mVelocityTracker.recycle(); 373 mVelocityTracker = null; 374 } 375 } 376 getCurrentVelocity()377 private float getCurrentVelocity() { 378 if (mVelocityTracker != null) { 379 mVelocityTracker.computeCurrentVelocity(1000); 380 return mVelocityTracker.getYVelocity(); 381 } else { 382 return 0f; 383 } 384 } 385 setEnabled(boolean enable)386 public void setEnabled(boolean enable) { 387 mEnabled = enable; 388 } 389 isEnabled()390 private boolean isEnabled() { 391 return mEnabled; 392 } 393 isFullyExpanded(ExpandableView underFocus)394 private boolean isFullyExpanded(ExpandableView underFocus) { 395 return underFocus.getIntrinsicHeight() == underFocus.getMaxContentHeight() 396 && (!underFocus.isSummaryWithChildren() || underFocus.areChildrenExpanded()); 397 } 398 399 @Override onTouchEvent(MotionEvent ev)400 public boolean onTouchEvent(MotionEvent ev) { 401 if (!isEnabled() && !mExpanding) { 402 // In case we're expanding we still want to finish the current motion. 403 return false; 404 } 405 trackVelocity(ev); 406 final int action = ev.getActionMasked(); 407 if (DEBUG_SCALE) Log.d(TAG, "touch: act=" + MotionEvent.actionToString(action) + 408 " expanding=" + mExpanding + 409 (0 != (mExpansionStyle & BLINDS) ? " (blinds)" : "") + 410 (0 != (mExpansionStyle & PULL) ? " (pull)" : "") + 411 (0 != (mExpansionStyle & STRETCH) ? " (stretch)" : "")); 412 413 mSGD.onTouchEvent(ev); 414 final int x = (int) mSGD.getFocusX(); 415 final int y = (int) mSGD.getFocusY(); 416 417 if (mOnlyMovements) { 418 mLastMotionY = ev.getRawY(); 419 return false; 420 } 421 switch (action) { 422 case MotionEvent.ACTION_DOWN: 423 mWatchingForPull = mScrollAdapter != null && 424 isInside(mScrollAdapter.getHostView(), x, y); 425 mResizedView = findView(x, y); 426 mInitialTouchX = ev.getRawX(); 427 mInitialTouchY = ev.getRawY(); 428 break; 429 case MotionEvent.ACTION_MOVE: { 430 if (mWatchingForPull) { 431 final float yDiff = ev.getRawY() - mInitialTouchY; 432 final float xDiff = ev.getRawX() - mInitialTouchX; 433 if (yDiff > mTouchSlop && yDiff > Math.abs(xDiff)) { 434 if (DEBUG) Log.v(TAG, "got venetian gesture (dy=" + yDiff + "px)"); 435 mWatchingForPull = false; 436 if (mResizedView != null && !isFullyExpanded(mResizedView)) { 437 if (startExpanding(mResizedView, BLINDS)) { 438 mInitialTouchY = ev.getRawY(); 439 mLastMotionY = ev.getRawY(); 440 mHasPopped = false; 441 } 442 } 443 } 444 } 445 if (mExpanding && 0 != (mExpansionStyle & BLINDS)) { 446 final float rawHeight = ev.getRawY() - mLastMotionY + mCurrentHeight; 447 final float newHeight = clamp(rawHeight); 448 boolean isFinished = false; 449 boolean expanded = false; 450 if (rawHeight > mNaturalHeight) { 451 isFinished = true; 452 expanded = true; 453 } 454 if (rawHeight < mSmallSize) { 455 isFinished = true; 456 expanded = false; 457 } 458 459 if (!mHasPopped) { 460 if (mEventSource != null) { 461 mEventSource.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); 462 } 463 mHasPopped = true; 464 } 465 466 mScaler.setHeight(newHeight); 467 mLastMotionY = ev.getRawY(); 468 if (isFinished) { 469 mCallback.expansionStateChanged(false); 470 } else { 471 mCallback.expansionStateChanged(true); 472 } 473 return true; 474 } 475 476 if (mExpanding) { 477 478 // Gestural expansion is running 479 updateExpansion(); 480 mLastMotionY = ev.getRawY(); 481 return true; 482 } 483 484 break; 485 } 486 487 case MotionEvent.ACTION_POINTER_UP: 488 case MotionEvent.ACTION_POINTER_DOWN: 489 if (DEBUG) Log.d(TAG, "pointer change"); 490 mInitialTouchY += mSGD.getFocusY() - mLastFocusY; 491 mInitialTouchSpan += mSGD.getCurrentSpan() - mLastSpanY; 492 break; 493 494 case MotionEvent.ACTION_UP: 495 case MotionEvent.ACTION_CANCEL: 496 if (DEBUG) Log.d(TAG, "up/cancel"); 497 finishExpanding(!isEnabled() || ev.getActionMasked() == MotionEvent.ACTION_CANCEL, 498 getCurrentVelocity()); 499 clearView(); 500 break; 501 } 502 mLastMotionY = ev.getRawY(); 503 maybeRecycleVelocityTracker(ev); 504 return mResizedView != null; 505 } 506 507 /** 508 * @return True if the view is expandable, false otherwise. 509 */ 510 @VisibleForTesting startExpanding(ExpandableView v, int expandType)511 boolean startExpanding(ExpandableView v, int expandType) { 512 if (!(v instanceof ExpandableNotificationRow)) { 513 return false; 514 } 515 mExpansionStyle = expandType; 516 if (mExpanding && v == mResizedView) { 517 return true; 518 } 519 mExpanding = true; 520 mCallback.expansionStateChanged(true); 521 if (DEBUG) Log.d(TAG, "scale type " + expandType + " beginning on view: " + v); 522 mCallback.setUserLockedChild(v, true); 523 mScaler.setView(v); 524 mOldHeight = mScaler.getHeight(); 525 mCurrentHeight = mOldHeight; 526 boolean canBeExpanded = mCallback.canChildBeExpanded(v); 527 if (canBeExpanded) { 528 if (DEBUG) Log.d(TAG, "working on an expandable child"); 529 mNaturalHeight = mScaler.getNaturalHeight(); 530 mSmallSize = v.getCollapsedHeight(); 531 } else { 532 if (DEBUG) Log.d(TAG, "working on a non-expandable child"); 533 mNaturalHeight = mOldHeight; 534 } 535 if (DEBUG) Log.d(TAG, "got mOldHeight: " + mOldHeight + 536 " mNaturalHeight: " + mNaturalHeight); 537 return true; 538 } 539 540 /** 541 * Finish the current expand motion 542 * @param forceAbort whether the expansion should be forcefully aborted and returned to the old 543 * state 544 * @param velocity the velocity this was expanded/ collapsed with 545 */ 546 @VisibleForTesting finishExpanding(boolean forceAbort, float velocity)547 void finishExpanding(boolean forceAbort, float velocity) { 548 finishExpanding(forceAbort, velocity, true /* allowAnimation */); 549 } 550 551 /** 552 * Finish the current expand motion 553 * @param forceAbort whether the expansion should be forcefully aborted and returned to the old 554 * state 555 * @param velocity the velocity this was expanded/ collapsed with 556 */ finishExpanding(boolean forceAbort, float velocity, boolean allowAnimation)557 private void finishExpanding(boolean forceAbort, float velocity, boolean allowAnimation) { 558 if (!mExpanding) return; 559 560 if (DEBUG) Log.d(TAG, "scale in finishing on view: " + mResizedView); 561 562 float currentHeight = mScaler.getHeight(); 563 final boolean wasClosed = (mOldHeight == mSmallSize); 564 boolean nowExpanded; 565 if (!forceAbort) { 566 if (wasClosed) { 567 nowExpanded = currentHeight > mOldHeight && velocity >= 0; 568 } else { 569 nowExpanded = currentHeight >= mOldHeight || velocity > 0; 570 } 571 nowExpanded |= mNaturalHeight == mSmallSize; 572 } else { 573 nowExpanded = !wasClosed; 574 } 575 if (mScaleAnimation.isRunning()) { 576 mScaleAnimation.cancel(); 577 } 578 mCallback.expansionStateChanged(false); 579 int naturalHeight = mScaler.getNaturalHeight(); 580 float targetHeight = nowExpanded ? naturalHeight : mSmallSize; 581 if (targetHeight != currentHeight && mEnabled && allowAnimation) { 582 mScaleAnimation.setFloatValues(targetHeight); 583 mScaleAnimation.setupStartValues(); 584 final View scaledView = mResizedView; 585 final boolean expand = nowExpanded; 586 mScaleAnimation.addListener(new AnimatorListenerAdapter() { 587 public boolean mCancelled; 588 589 @Override 590 public void onAnimationEnd(Animator animation) { 591 if (!mCancelled) { 592 mCallback.setUserExpandedChild(scaledView, expand); 593 if (!mExpanding) { 594 mScaler.setView(null); 595 } 596 } else { 597 mCallback.setExpansionCancelled(scaledView); 598 } 599 mCallback.setUserLockedChild(scaledView, false); 600 mScaleAnimation.removeListener(this); 601 } 602 603 @Override 604 public void onAnimationCancel(Animator animation) { 605 mCancelled = true; 606 } 607 }); 608 velocity = nowExpanded == velocity >= 0 ? velocity : 0; 609 mFlingAnimationUtils.apply(mScaleAnimation, currentHeight, targetHeight, velocity); 610 mScaleAnimation.start(); 611 } else { 612 if (targetHeight != currentHeight) { 613 mScaler.setHeight(targetHeight); 614 } 615 mCallback.setUserExpandedChild(mResizedView, nowExpanded); 616 mCallback.setUserLockedChild(mResizedView, false); 617 mScaler.setView(null); 618 } 619 620 mExpanding = false; 621 mExpansionStyle = NONE; 622 623 if (DEBUG) Log.d(TAG, "wasClosed is: " + wasClosed); 624 if (DEBUG) Log.d(TAG, "currentHeight is: " + currentHeight); 625 if (DEBUG) Log.d(TAG, "mSmallSize is: " + mSmallSize); 626 if (DEBUG) Log.d(TAG, "targetHeight is: " + targetHeight); 627 if (DEBUG) Log.d(TAG, "scale was finished on view: " + mResizedView); 628 } 629 clearView()630 private void clearView() { 631 mResizedView = null; 632 } 633 634 /** 635 * Use this to abort any pending expansions in progress and force that there will be no 636 * animations. 637 */ cancelImmediately()638 public void cancelImmediately() { 639 cancel(false /* allowAnimation */); 640 } 641 642 /** 643 * Use this to abort any pending expansions in progress. 644 */ cancel()645 public void cancel() { 646 cancel(true /* allowAnimation */); 647 } 648 cancel(boolean allowAnimation)649 private void cancel(boolean allowAnimation) { 650 finishExpanding(true /* forceAbort */, 0f /* velocity */, allowAnimation); 651 clearView(); 652 653 // reset the gesture detector 654 mSGD = new ScaleGestureDetector(mContext, mScaleGestureListener); 655 } 656 657 /** 658 * Change the expansion mode to only observe movements and don't perform any resizing. 659 * This is needed when the expanding is finished and the scroller kicks in, 660 * performing an overscroll motion. We only want to shrink it again when we are not 661 * overscrolled. 662 * 663 * @param onlyMovements Should only movements be observed? 664 */ onlyObserveMovements(boolean onlyMovements)665 public void onlyObserveMovements(boolean onlyMovements) { 666 mOnlyMovements = onlyMovements; 667 } 668 } 669 670