/** * 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); }
/** * Handles a touch event on the CallCard. * * @see CallCard.dispatchTouchEvent */ /* package */ void handleCallCardTouchEvent(MotionEvent ev) { // if (DBG) log("handleCallCardTouchEvent(" + ev + ")..."); if (mInCallScreen == null || mInCallScreen.isFinishing()) { Log.i(LOG_TAG, "handleCallCardTouchEvent: InCallScreen gone; ignoring touch..."); return; } final int action = ev.getAction(); // All the sliding code depends on deltas, so it // doesn't really matter in what coordinate space // we are, as long as it's independant of our position final int xAbsolute = (int) ev.getRawX(); final int yAbsolute = (int) ev.getRawY(); if (isSlideInProgress()) { if (SystemClock.elapsedRealtime() - mTouchDownTime > 1000) abortSlide(); else switch (action) { case MotionEvent.ACTION_DOWN: // Shouldn't happen in this state. break; case MotionEvent.ACTION_MOVE: // Move the CallCard! updateWhileSliding(yAbsolute); break; case MotionEvent.ACTION_UP: // See if we've slid far enough to do some action // (ie. hang up, or answer an incoming call, // depending on our current state.) stopSliding(yAbsolute); break; case MotionEvent.ACTION_CANCEL: // Because we set the FLAG_IGNORE_CHEEK_PRESSES // WindowManager flag (see init()), we'll get an // ACTION_CANCEL event if a valid ACTION_DOWN is later // followed by an ACTION_MOVE that's a "fat touch". // In this case, abort the slide. if (DBG) log("handleCallCardTouchEvent: ACTION_CANCEL: " + ev); abortSlide(); break; } } else { switch (action) { case MotionEvent.ACTION_DOWN: // This event is a touch DOWN on the card: start sliding! startSliding(xAbsolute, yAbsolute); break; case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: // If we're not sliding (yet), ignore anything other than // ACTION_DOWN. break; } } }
/** * Sets the text of the "slide hints" based on the specified resource IDs. (A resource ID of zero * means "hide the hint and arrow".) */ private void setSlideHints(int upHintResId, int downHintResId) { // TODO: It would probably look really cool to do a "fade in" animation // when a hint becomes visible after previously being hidden, rather // than having it just pop on. // TODO: Also consider having both slide hints *always* visible, // so that as you slide you first cover up one and later reveal // the other. But this is tricky: the text of the hint that // "starts off hidden" will need to be pre-set to the value it // should have *after* the slide is complete. if (DBG) { String upHint = (upHintResId != 0) ? mInCallScreen.getString(upHintResId) : "<empty>"; String downHint = (downHintResId != 0) ? mInCallScreen.getString(downHintResId) : "<empty>"; log("setSlideHints: UP '" + upHint + "', DOWN '" + downHint + "'"); } mSlideUp.setVisibility((upHintResId != 0) ? View.VISIBLE : View.GONE); if (upHintResId != 0) mSlideUpHint.setText(upHintResId); mSlideDown.setVisibility((downHintResId != 0) ? View.VISIBLE : View.GONE); if (downHintResId != 0) mSlideDownHint.setText(downHintResId); }
/** * The user successfully completed a "slide" operation. Activate whatever action the slide was * supposed to trigger. * * <p>(That could either be (1) hang up the ongoing call(s), or (2) answer an incoming call.) * * <p>This method is responsible for triggering any screen updates that need to happen, based on * any internal state changes due to the slide. */ private void finishSuccessfulSlide() { if (DBG) log("finishSuccessfulSlide()..."); mSlideInProgress = false; // TODO: Need to test lots of possible edge cases here, like if the // state of the Phone changes while the slide was happening. // (For example, say the user slid the card UP to answer an incoming // call, but the phone's no longer ringing by the time we got here...) // TODO: The state-checking logic here is very similar to the logic in // updateCardSlideHints(). Rather than duplicating this logic in both // places, maybe use a single helper function that generates a // complete "slidability matrix" (ie. all slide hints / states / // actions) based on the current state of the Phone. boolean phoneStateAboutToChange = false; // Perform the "successful slide" action. if (mCardAtTop) { // The downward slide action is to hang up any ongoing // call(s). if (DBG) log(" =========> Slide complete: HANGING UP..."); mInCallScreen.reject(); // Any "hangup" action is going to cause // the Phone state to change imminently. phoneStateAboutToChange = true; } else { // The upward slide action is to answer the incoming call. // (We put the ongoing call on hold if there's already one line in // use, or hang up the ongoing call if both lines are in use.) if (DBG) log(" =========> Slide complete: ANSWERING..."); mInCallScreen.answer(); // Either of the "answer call" functions is going to cause // the Phone state to change imminently. phoneStateAboutToChange = true; } // Finally, update the state of the UI depending on what just happened. // Update the "permanent" position of the sliding card, and the slide // hints. // // But *don't* do this if we know the Phone state's about to change, // like if the user just did a "slide up to answer". In that case // we know we're going to get a onPhoneStateChanged() call in a few // milliseconds, and *that's* going to result in an updateScreen() call. // (And if we were to do that update now, we'd just get a brief flash // of the card at the bottom or the screen. So don't do anything // here.) if (!phoneStateAboutToChange) { updateCardPreferredPosition(); updateCardSlideHints(); // And force an immmediate re-layout. (No need to do any // animation here, since the card's new "preferred position" is // exactly where the user just slid it.) mMainFrame.requestLayout(); } }