/**
   * Handle a MOVE event while sliding.
   *
   * @param y the y-position of the MOVE event in absolute screen coordinates.
   */
  /* package */ void updateWhileSliding(int y) {
    int totalSlideAmount = y - mTouchDownY;
    // if (DBG) log("--------------> MOTION! y = " + y
    //              + "  (total slide = " + totalSlideAmount + ")");

    // TODO: consider caching popupTopPosY and popupBottomPosY in
    // startSliding (to save a couple of method calls each time here.)
    // TODO: this block is duplicated above; use a single helper method instead.
    mMainFrame.getLocationInWindow(mTempLocation);
    final int mainFrameX = mTempLocation[0];
    final int mainFrameY = 0; // mTempLocation[1];

    // In the "top" position the CallCard is aligned exactly with the
    // top edge of the main frame.
    final int popupTopPosY = mainFrameY;

    // And in the "bottom" position, the bottom of the CallCard is
    // aligned exactly with the bottom of the main frame.
    final int popupBottomPosY = mainFrameY + mMainFrame.getHeight() - height;

    // Forcibly reposition the CallCard
    int newCardTop = mCardPreferredY + totalSlideAmount;
    // if (DBG) log("  ==> New card top: " + newCardTop);

    // But never slide *beyond* the topmost or bottom-most position:
    if (newCardTop < popupTopPosY) newCardTop = popupTopPosY;
    else if (newCardTop > popupBottomPosY) newCardTop = popupBottomPosY;

    // Forcibly reposition the PopupWindow.
    mCallCard.update(mCardPreferredX, newCardTop, -1, -1);
  }
  /**
   * Bail out of an in-progress slide *without* activating whatever action the slide was supposed to
   * trigger.
   */
  private void abortSlide() {
    if (DBG) log("abortSlide()...");

    mSlideInProgress = false;

    // This slide had no effect.  Nothing about the state of the
    // UI has changed, so no need to updateCardPreferredPosition() or
    // updateCardSlideHints().  But we *do* need to reposition the
    // PopupWindow back in its correct position.

    // TODO: smoothly animate back to the preferred position.
    mCallCard.update(mCardPreferredX, mCardPreferredY, -1, -1);
  }
  /**
   * Initializes the internal state of the SlidingCardManager.
   *
   * @param phone the Phone app's Phone instance
   * @param inCallScreen the InCallScreen activity
   * @param mainFrame the InCallScreen's main frame containing the in-call UI elements
   */
  /* package */ public void init(Phone phone, InCallScreen inCallScreen, ViewGroup mainFrame) {
    if (DBG) log("init()...");
    mPhone = phone;
    mInCallScreen = inCallScreen;
    mMainFrame = mainFrame;

    // Slide hints
    mSlideUp = (ViewGroup) mInCallScreen.findViewById(R.id.slideUp);
    mSlideUpHint = (TextView) mInCallScreen.findViewById(R.id.slideUpHint);
    mSlideDown = (ViewGroup) mInCallScreen.findViewById(R.id.slideDown);
    mSlideDownHint = (TextView) mInCallScreen.findViewById(R.id.slideDownHint);

    mCallCard = (CallCard) mMainFrame.findViewById(R.id.callCard);
    mCallCard.setSlidingCardManager(this);
  }
  /**
   * Update the "permanent" position (ie. the actual layout params) of the sliding card based on the
   * current call state.
   *
   * <p>This method sets mCardAtTop, mCallEndedState, mCardPreferredX, and mCardPreferredY. It also
   * repositions the PopupWindow if it's showing.
   *
   * <p>Note that *while sliding* we manually reposition the card on every motion event that comes
   * in. The x/y position we set here determines where the card should be while *not* sliding.
   *
   * <p>TODO: If the card position changes for some reason *other* than user action (i.e. as a
   * result of an onPhoneStateChanged() callback), we should smoothly animate the position change!
   * (For example, if you're in a call and the other end hangs up, the card should switch to "Call
   * ended" mode and smoothly animate to the bottom position.)
   */
  public /* package */ void updateCardPreferredPosition() {
    if (DBG) log("updateCardPreferredPosition()...");
    // if (DBG) log("- card's LayoutParams: " + mCallCard.getLayoutParams());

    // Bail out if our View hierarchy isn't attached to a Window yet
    // (since the mMainFrame.getLocationOnScreen() call below
    // will fail.)
    if (mMainFrame.getWindowToken() == null) {
      if (DBG) log("updateCardPreferredPosition: View hierarchy unattached; bailing...");
      return;
    }

    /*
    if (mMainFrame.getHeight() == 0) {
        // The code here needs to know the sizes and positions of some
        // views in the mMainFrame view hierarchy, so you're only
        // allowed to call this method *after* the whole in-call UI
        // has been measured and laid out.
        // (This is why we defer calling showPopup() until an
        // onGlobalLayout() call comes in.)
        throw new IllegalStateException(
                "updateCardPreferredPosition: main frame not measured yet");
    }
    */

    // Given the current state of the Phone and the UI, decide whether
    // the card should be at the TOP or BOTTOM of the screen right now.

    // Compute the possible coordinates onscreen for the popup.
    // TODO: this block is duplicated below; use a single helper method instead.
    mMainFrame.getLocationInWindow(mTempLocation);
    final int mainFrameX = mTempLocation[0];
    final int mainFrameY = 0; // mTempLocation[1];
    if (DBG) log("- mMainFrame loc in window: " + mainFrameX + ", " + mainFrameY);

    // In the "top" position the CallCard is aligned exactly with the
    // top edge of the main frame.
    final int popupTopPosY = mainFrameY;

    // And in the "bottom" position, the bottom of the CallCard is
    // aligned exactly with the bottom of the main frame.
    if (height == 0) {
      height = mCallCard.getHeight();
      // Reposition the "slide hints".
      RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) mSlideUp.getLayoutParams();
      // Equivalent to setting android:layout_marginTop in XML
      lp.bottomMargin = height;
      mSlideUp.setLayoutParams(lp);
      lp = (RelativeLayout.LayoutParams) mSlideDown.getLayoutParams();
      // Equivalent to setting android:layout_marginTop in XML
      lp.topMargin = height;
      mSlideDown.setLayoutParams(lp);
      height += 10;
    }
    final int popupBottomPosY = mainFrameY + mMainFrame.getHeight() - height;

    if (Receiver.ccCall != null && Receiver.ccCall.getState() != Call.State.DISCONNECTED) {
      // When the phone is in use, the position of the card is
      // determined solely by whether an incoming call is ringing or
      // not.
      final boolean hasRingingCall = Receiver.ccCall.getState() == Call.State.INCOMING;
      mCardAtTop = !hasRingingCall;
      mCallEndedState = false;
    } else {
      // Phone is completely idle!  Display the CALL ENDED state
      // with the card at the bottom of the screen.
      mCardAtTop = false;
      mCallEndedState = true;
    }
    mCardPreferredX = mainFrameX;
    mCardPreferredY = mCardAtTop ? popupTopPosY : popupBottomPosY;

    if (DBG)
      log(
          "==> Setting card preferred position (mCardAtTop = "
              + mCardAtTop
              + ") to: "
              + mCardPreferredX
              + ", "
              + mCardPreferredY);

    // This is a no-op if the PopupWindow isn't showing.
    mCallCard.update(mCardPreferredX, mCardPreferredY, -1, -1);
  }