Fix 2209086: Make animation/decline transition much smoother in PhoneApp.

This fix makes hideIncomingCallWidget() re-entrant such that repeatedly calling it while
the incoming call widget is being hidden is OK. This fixes a problem where we might try to
hide the incoming call widget a second time in the case where updateState() is called again
based on some async event.

Tested: Answer/Decline call on CDMA and GSM device.
diff --git a/src/com/android/phone/InCallTouchUi.java b/src/com/android/phone/InCallTouchUi.java
index 1494478..0359b22 100644
--- a/src/com/android/phone/InCallTouchUi.java
+++ b/src/com/android/phone/InCallTouchUi.java
@@ -272,7 +272,13 @@
             throw new IllegalStateException(
                 "'Incoming' and 'in-call' touch controls visible at the same time!");
         }
-        mIncomingCallWidget.setVisibility(showIncomingCallControls ? View.VISIBLE : View.GONE);
+
+        if (showIncomingCallControls) {
+            showIncomingCallWidget();
+        } else {
+            hideIncomingCallWidget();
+        }
+
         mInCallControls.setVisibility(showInCallControls ? View.VISIBLE : View.GONE);
 
         // TODO: As an optimization, also consider setting the visibility
@@ -551,18 +557,14 @@
 
     /**
      * Apply an animation to hide the incoming call widget.
-     *
-     * NOTE: in addition to this fadeout animation, the {@link #mIncomingCallWidget}
-     * will get hidden by the updateState() method if a phone state change event comes
-     * in and the phone isn't in the RINGING state any more. So there's basically a race
-     * condition between this animation, and the telephony layer actually answering
-     * the incoming call.
-     *
-     * In practice, 250ms should be short enough to avoid this condition, but this should
-     * probably be cleaned up post-eclair.
      */
     private void hideIncomingCallWidget() {
-        // Transition from the incoming call UI
+        if (mIncomingCallWidget.getVisibility() != View.VISIBLE
+                || mIncomingCallWidget.getAnimation() != null) {
+            // Widget is already hidden or in the process of being hidden
+            return;
+        }
+        // Hide the incoming call screen with a transition
         AlphaAnimation anim = new AlphaAnimation(1.0f, 0.0f);
         anim.setDuration(IN_CALL_WIDGET_TRANSITION_TIME);
         anim.setAnimationListener(new AnimationListener() {
@@ -577,6 +579,7 @@
 
             public void onAnimationEnd(Animation animation) {
                 // hide the incoming call UI.
+                mIncomingCallWidget.clearAnimation();
                 mIncomingCallWidget.setVisibility(View.GONE);
             }
         });
@@ -584,6 +587,18 @@
     }
 
     /**
+     * Shows the incoming call widget and cancels any animation that may be fading it out.
+     */
+    private void showIncomingCallWidget() {
+        Animation anim = mIncomingCallWidget.getAnimation();
+        if (anim != null) {
+            anim.reset();
+            mIncomingCallWidget.clearAnimation();
+        }
+        mIncomingCallWidget.setVisibility(View.VISIBLE);
+    }
+
+    /**
      * Handles state changes of the SlidingTabSelector widget.  While the user
      * is dragging one of the handles, we display an onscreen hint; see
      * CallCard.getRotateWidgetHint().