Merge "Remove CircularRevealActivity"
diff --git a/InCallUI/res/layout/primary_call_info.xml b/InCallUI/res/layout/primary_call_info.xml
index 71369d4..cb9cc24 100644
--- a/InCallUI/res/layout/primary_call_info.xml
+++ b/InCallUI/res/layout/primary_call_info.xml
@@ -109,7 +109,8 @@
                     android:textColor="@color/incall_call_banner_subtext_color"
                     android:textSize="@dimen/call_label_text_size"
                     android:singleLine="true"
-                    android:textDirection="ltr" />
+                    android:textDirection="ltr"
+                    android:visibility="gone" />
 
                 <TextView android:id="@+id/phoneNumber"
                     android:layout_width="match_parent"
@@ -118,7 +119,8 @@
                     android:textAppearance="?android:attr/textAppearanceSmall"
                     android:textColor="@color/incall_call_banner_subtext_color"
                     android:textSize="@dimen/call_label_text_size"
-                    android:singleLine="true" />
+                    android:singleLine="true"
+                    android:visibility="gone" />
 
         </LinearLayout>
 
@@ -145,6 +147,7 @@
         android:textAppearance="?android:attr/textAppearanceSmall"
         android:textColor="@color/incall_call_banner_text_color"
         android:maxLines="1"
-        android:ellipsize="end" />
+        android:ellipsize="end"
+        android:visibility="gone" />
 
 </LinearLayout>  <!-- End of call_banner -->
diff --git a/InCallUI/res/values/styles.xml b/InCallUI/res/values/styles.xml
index e16d72c..dba7cc3 100644
--- a/InCallUI/res/values/styles.xml
+++ b/InCallUI/res/values/styles.xml
@@ -70,43 +70,12 @@
         <item name="android:textOff">@null</item>
     </style>
 
-    <style name="InCallAnimationStyle" parent="@android:style/Animation.Activity">
-        <!-- Suppress task-to-task animation happening during the transition from
-             OutgoingCallBroadcaster (and SipOptionHandler) to InCallActivity.
-             The transition unexpectedly happens during the transition (inside the phone task),
-             because InCallActivity is using android:launchMode="singleInstance".
-
-             - taskOpenEnterAnimation/taskOpenExitAnimation is used for the first time
-               InCallActivity instance is created.
-
-             - taskToFrontEnterAnimation/taskToFrontExitAnimation is used when InCallActivity
-               is already available.
-               (Note that InCallActivity won't be destroyed once it is created)
-
-             TODO: try removing the flag instead -->
-        <item name="android:taskOpenEnterAnimation">@null</item>
-        <item name="android:taskOpenExitAnimation">@anim/activity_open_exit</item>
-        <item name="android:taskToFrontEnterAnimation">@anim/activity_open_enter</item>
-        <item name="android:taskToFrontExitAnimation">@anim/activity_open_exit</item>
-    </style>
-
-    <style name="OutgoingCallAnimationStyle" parent="@android:style/Animation.Activity">
-        <item name="android:taskOpenEnterAnimation">@null</item>
-        <item name="android:taskOpenExitAnimation">@null</item>
-        <item name="android:activityOpenEnterAnimation">@null</item>
-        <item name="android:activityOpenExitAnimation">@null</item>
-        <item name="android:activityCloseEnterAnimation">@null</item>
-        <item name="android:activityCloseExitAnimation">@null</item>
-        <item name="android:taskToFrontEnterAnimation">@null</item>
-        <item name="android:taskToFrontExitAnimation">@null</item>
-    </style>
-
     <!-- Theme for the InCallActivity activity. Should have a transparent background for the
          circular reveal animation for a new outgoing call to work correctly. We don't just use
          Theme.Black.NoTitleBar directly, since we want any popups or dialogs from the
          InCallActivity to have the correct Material style. -->
     <style name="Theme.InCallScreen" parent="@android:style/Theme.Material.Light">
-        <item name="android:windowAnimationStyle">@style/InCallAnimationStyle</item>
+        <item name="android:windowAnimationStyle">@null</item>
         <item name="android:windowIsTranslucent">true</item>
         <item name="android:windowBackground">@android:color/transparent</item>
         <item name="android:windowContentOverlay">@null</item>
@@ -118,15 +87,6 @@
         <item name="android:buttonStyleToggle">@style/InCallCompoundButton</item>
     </style>
 
-    <style name="Theme.CircularRevealAnimation" parent="@android:style/Theme.Material.Light">
-        <item name="android:windowIsTranslucent">true</item>
-        <item name="android:windowContentOverlay">@null</item>
-        <item name="android:windowNoTitle">true</item>
-        <item name="android:colorPrimaryDark">@color/dialer_theme_color_dark</item>
-        <item name="android:windowBackground">@android:color/transparent</item>
-        <item name="android:windowAnimationStyle">@null</item>
-    </style>
-
     <style name="InCallPopupMenuStyle" parent="@android:style/Theme.Material.Light">
         <item name="android:textColorPrimary">@color/popup_menu_color</item>
     </style>
diff --git a/InCallUI/src/com/android/incallui/CallCardFragment.java b/InCallUI/src/com/android/incallui/CallCardFragment.java
index 649176c..00e6196 100644
--- a/InCallUI/src/com/android/incallui/CallCardFragment.java
+++ b/InCallUI/src/com/android/incallui/CallCardFragment.java
@@ -24,7 +24,6 @@
 import android.app.Activity;
 import android.content.Context;
 import android.content.res.Configuration;
-import android.graphics.Point;
 import android.graphics.drawable.AnimationDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
@@ -33,11 +32,9 @@
 import android.telephony.PhoneNumberUtils;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
-import android.view.Display;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnLayoutChangeListener;
-import android.view.ViewAnimationUtils;
 import android.view.ViewGroup;
 import android.view.ViewPropertyAnimator;
 import android.view.ViewTreeObserver;
@@ -51,7 +48,6 @@
 
 import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
 import com.android.contacts.common.widget.FloatingActionButtonController;
-import com.android.incallui.service.PhoneNumberService;
 import com.android.phone.common.animation.AnimUtils;
 
 import java.util.List;
@@ -63,7 +59,6 @@
         implements CallCardPresenter.CallCardUi {
 
     private AnimatorSet mAnimatorSet;
-    private int mRevealAnimationDuration;
     private int mShrinkAnimationDuration;
     private int mFabNormalDiameter;
     private int mFabSmallDiameter;
@@ -108,9 +103,6 @@
     private ImageButton mFloatingActionButton;
     private int mFloatingActionButtonVerticalOffset;
 
-    // Cached DisplayMetrics density.
-    private float mDensity;
-
     private float mTranslationOffset;
     private Animation mPulseAnimation;
 
@@ -132,7 +124,6 @@
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        mRevealAnimationDuration = getResources().getInteger(R.integer.reveal_animation_duration);
         mShrinkAnimationDuration = getResources().getInteger(R.integer.shrink_animation_duration);
         mVideoAnimationDuration = getResources().getInteger(R.integer.video_animation_duration);
         mFloatingActionButtonVerticalOffset = getResources().getDimensionPixelOffset(
@@ -156,9 +147,6 @@
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
-        super.onCreateView(inflater, container, savedInstanceState);
-
-        mDensity = getResources().getDisplayMetrics().density;
         mTranslationOffset =
                 getResources().getDimensionPixelSize(R.dimen.call_card_anim_translate_y_offset);
 
@@ -230,6 +218,9 @@
 
         mPrimaryName.setElegantTextHeight(false);
         mCallStateLabel.setElegantTextHeight(false);
+
+        final LayoutTransition transition = mPrimaryCallInfo.getLayoutTransition();
+        transition.enableTransitionType(LayoutTransition.CHANGING);
     }
 
     @Override
@@ -425,7 +416,6 @@
     public void setPrimary(String number, String name, boolean nameIsNumber, String label,
             Drawable photo, boolean isSipCall) {
         Log.d(this, "Setting primary call");
-
         // set the name field.
         setPrimaryName(name, nameIsNumber);
 
@@ -842,13 +832,14 @@
         }
     }
 
-    public void animateForNewOutgoingCall(final Point touchPoint,
-            final boolean showCircularReveal) {
+    @Override
+    public void animateForNewOutgoingCall() {
         final ViewGroup parent = (ViewGroup) mPrimaryCallCardContainer.getParent();
 
         final ViewTreeObserver observer = getView().getViewTreeObserver();
 
-        mPrimaryCallInfo.getLayoutTransition().disableTransitionType(LayoutTransition.CHANGING);
+        final LayoutTransition transition = mPrimaryCallInfo.getLayoutTransition();
+        transition.disableTransitionType(LayoutTransition.CHANGING);
 
         observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
             @Override
@@ -862,21 +853,21 @@
                 final LayoutIgnoringListener listener = new LayoutIgnoringListener();
                 mPrimaryCallCardContainer.addOnLayoutChangeListener(listener);
 
-                // Prepare the state of views before the circular reveal animation
+                // Prepare the state of views before the slide animation
                 final int originalHeight = mPrimaryCallCardContainer.getHeight();
                 mPrimaryCallCardContainer.setBottom(parent.getHeight());
 
                 // Set up FAB.
                 mFloatingActionButtonContainer.setVisibility(View.GONE);
                 mFloatingActionButtonController.setScreenWidth(parent.getWidth());
+
                 mCallButtonsContainer.setAlpha(0);
                 mCallStateLabel.setAlpha(0);
                 mPrimaryName.setAlpha(0);
                 mCallTypeLabel.setAlpha(0);
                 mCallNumberAndLabel.setAlpha(0);
 
-                final Animator animator = getOutgoingCallAnimator(touchPoint,
-                        parent.getHeight(), originalHeight, showCircularReveal);
+                final Animator animator = getShrinkAnimator(parent.getHeight(), originalHeight);
 
                 animator.addListener(new AnimatorListenerAdapter() {
                     @Override
@@ -990,41 +981,6 @@
         return shrinkAnimator;
     }
 
-    private Animator getRevealAnimator(Point touchPoint) {
-        final Activity activity = getActivity();
-        final View view  = activity.getWindow().getDecorView();
-        final Display display = activity.getWindowManager().getDefaultDisplay();
-        final Point size = new Point();
-        display.getSize(size);
-
-        int startX = size.x / 2;
-        int startY = size.y / 2;
-        if (touchPoint != null) {
-            startX = touchPoint.x;
-            startY = touchPoint.y;
-        }
-
-        final Animator valueAnimator = ViewAnimationUtils.createCircularReveal(view,
-                startX, startY, 0, Math.max(size.x, size.y));
-        valueAnimator.setDuration(mRevealAnimationDuration);
-        return valueAnimator;
-    }
-
-    private Animator getOutgoingCallAnimator(Point touchPoint, int startHeight, int endHeight,
-            boolean showCircularReveal) {
-
-        final Animator shrinkAnimator = getShrinkAnimator(startHeight, endHeight);
-
-        if (!showCircularReveal) {
-            return shrinkAnimator;
-        }
-
-        final Animator revealAnimator = getRevealAnimator(touchPoint);
-        final AnimatorSet animatorSet = new AnimatorSet();
-        animatorSet.playSequentially(revealAnimator, shrinkAnimator);
-        return animatorSet;
-    }
-
     private void assignTranslateAnimation(View view, int offset) {
         view.setTranslationY(mTranslationOffset * offset);
         view.animate().translationY(0).alpha(1).withLayer()
@@ -1045,7 +1001,10 @@
         setViewStatePostAnimation(mCallStateIcon);
 
         mPrimaryCallCardContainer.removeOnLayoutChangeListener(layoutChangeListener);
-        mPrimaryCallInfo.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
+
+        final LayoutTransition transition = mPrimaryCallInfo.getLayoutTransition();
+        transition.enableTransitionType(LayoutTransition.CHANGING);
+
         mFloatingActionButtonController.scaleIn(AnimUtils.NO_DELAY);
     }
 
diff --git a/InCallUI/src/com/android/incallui/CallCardPresenter.java b/InCallUI/src/com/android/incallui/CallCardPresenter.java
index cb378a6..ab93c2d 100644
--- a/InCallUI/src/com/android/incallui/CallCardPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallCardPresenter.java
@@ -17,6 +17,8 @@
 package com.android.incallui;
 
 import android.Manifest;
+import android.app.Activity;
+import android.app.FragmentManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -34,6 +36,7 @@
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 
+import com.android.incallui.CircularRevealFragment.OnCircularRevealCompleteListener;
 import com.android.incallui.ContactInfoCache.ContactCacheEntry;
 import com.android.incallui.ContactInfoCache.ContactInfoCacheCallback;
 import com.android.incallui.InCallPresenter.InCallDetailsListener;
@@ -743,5 +746,6 @@
         void setProgressSpinnerVisible(boolean visible);
         void showManageConferenceCallButton(boolean visible);
         boolean isManageConferenceVisible();
+        void animateForNewOutgoingCall();
     }
 }
diff --git a/InCallUI/src/com/android/incallui/CircularRevealActivity.java b/InCallUI/src/com/android/incallui/CircularRevealActivity.java
deleted file mode 100644
index 4dc58ba..0000000
--- a/InCallUI/src/com/android/incallui/CircularRevealActivity.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.incallui;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.app.Activity;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.graphics.Outline;
-import android.graphics.Point;
-import android.os.Bundle;
-import android.support.v4.content.LocalBroadcastManager;
-import android.view.Display;
-import android.view.View;
-import android.view.ViewAnimationUtils;
-import android.view.ViewOutlineProvider;
-import android.view.ViewTreeObserver;
-import android.view.ViewTreeObserver.OnPreDrawListener;
-
-import com.android.contacts.common.interactions.TouchPointManager;
-import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
-
-/**
- * Lightweight activity used to display a circular reveal while InCallActivity is starting up.
- * A BroadcastReceiver is used to listen to broadcasts from a LocalBroadcastManager to finish
- * the activity at suitable times.
- */
-public class CircularRevealActivity extends Activity {
-    private static final int REVEAL_DURATION = 333;
-    public static final String EXTRA_THEME_COLORS = "extra_theme_colors";
-    public static final String ACTION_CLEAR_DISPLAY = "action_clear_display";
-
-    final BroadcastReceiver mClearDisplayReceiver = new BroadcastReceiver( ) {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            clearDisplay();
-        }
-    };
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        overridePendingTransition(0, 0);
-        setContentView(R.layout.outgoing_call_animation);
-        prepareDecorViewFromIntent(getIntent());
-    }
-
-    @Override
-    protected void onStart() {
-        super.onStart();
-        if (!InCallPresenter.getInstance().isServiceBound()) {
-            clearDisplay();
-        }
-        final IntentFilter filter = new IntentFilter();
-        filter.addAction(ACTION_CLEAR_DISPLAY);
-        LocalBroadcastManager.getInstance(this).registerReceiver(mClearDisplayReceiver, filter);
-    }
-
-    @Override
-    protected void onStop() {
-        LocalBroadcastManager.getInstance(this).unregisterReceiver(mClearDisplayReceiver);
-        super.onStop();
-    }
-
-    @Override
-    protected void onNewIntent(Intent intent) {
-        setIntent(intent);
-        prepareDecorViewFromIntent(intent);
-    }
-
-    private void prepareDecorViewFromIntent(Intent intent) {
-        final Point touchPoint = intent.getParcelableExtra(TouchPointManager.TOUCH_POINT);
-        final MaterialPalette palette = intent.getParcelableExtra(EXTRA_THEME_COLORS);
-        setupDecorView(touchPoint, palette);
-    }
-
-    private void setupDecorView(final Point touchPoint, MaterialPalette palette) {
-        final View view  = getWindow().getDecorView();
-
-        // The circle starts from an initial size of 0 so clip it such that it is invisible. When
-        // the animation later starts, this clip will be clobbered by the circular reveal clip.
-        // See ViewAnimationUtils.createCircularReveal.
-        view.setOutlineProvider(new ViewOutlineProvider() {
-            @Override
-            public void getOutline(View view, Outline outline) {
-                // Using (0, 0, 0, 0) will not work since the outline will simply be treated as
-                // an empty outline.
-                outline.setOval(-1, -1, 0, 0);
-            }
-        });
-        view.setClipToOutline(true);
-
-        if (palette != null) {
-            view.findViewById(R.id.outgoing_call_animation_circle).setBackgroundColor(
-                    palette.mPrimaryColor);
-            getWindow().setStatusBarColor(palette.mSecondaryColor);
-        }
-
-        view.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
-            @Override
-            public boolean onPreDraw() {
-                final ViewTreeObserver vto = view.getViewTreeObserver();
-                if (vto.isAlive()) {
-                    vto.removeOnPreDrawListener(this);
-                }
-                final Animator animator = getRevealAnimator(touchPoint);
-                // Since this animator is a RenderNodeAnimator (native animator), add an arbitary
-                // start delay to force the onAnimationStart callback to happen later on the UI
-                // thread. Otherwise it would happen right away inside animator.start()
-                animator.setStartDelay(5);
-                animator.addListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationStart(Animator animation) {
-                        InCallPresenter.getInstance().onCircularRevealStarted(
-                                CircularRevealActivity.this);
-                    }
-
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        view.setClipToOutline(false);
-                        super.onAnimationEnd(animation);
-                    }
-                });
-                animator.start();
-                return false;
-            }
-        });
-    }
-
-    private void clearDisplay() {
-        getWindow().getDecorView().setVisibility(View.INVISIBLE);
-        finish();
-    }
-
-    @Override
-    public void onBackPressed() {
-        return;
-    }
-
-    public static void sendClearDisplayBroadcast(Context context) {
-        LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent(ACTION_CLEAR_DISPLAY));
-    }
-
-    private Animator getRevealAnimator(Point touchPoint) {
-        final View view  = getWindow().getDecorView();
-        final Display display = getWindowManager().getDefaultDisplay();
-        final Point size = new Point();
-        display.getSize(size);
-
-        int startX = size.x / 2;
-        int startY = size.y / 2;
-        if (touchPoint != null) {
-            startX = touchPoint.x;
-            startY = touchPoint.y;
-        }
-
-        final Animator valueAnimator = ViewAnimationUtils.createCircularReveal(view,
-                startX, startY, 0, Math.max(size.x, size.y));
-        valueAnimator.setDuration(REVEAL_DURATION);
-        return valueAnimator;
-    }
-}
diff --git a/InCallUI/src/com/android/incallui/CircularRevealFragment.java b/InCallUI/src/com/android/incallui/CircularRevealFragment.java
new file mode 100644
index 0000000..c282179
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/CircularRevealFragment.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.incallui;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.graphics.Outline;
+import android.graphics.Point;
+import android.os.Bundle;
+import android.view.Display;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewAnimationUtils;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnPreDrawListener;
+
+import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
+
+public class CircularRevealFragment extends Fragment {
+    static final String TAG = "CircularRevealFragment";
+
+    private Point mTouchPoint;
+    private OnCircularRevealCompleteListener mListener;
+    private boolean mAnimationStarted;
+
+    interface OnCircularRevealCompleteListener {
+        public void onCircularRevealComplete(FragmentManager fm);
+    }
+
+    public static void startCircularReveal(FragmentManager fm, Point touchPoint,
+            OnCircularRevealCompleteListener listener) {
+        fm.beginTransaction().add(R.id.main, new CircularRevealFragment(touchPoint, listener), TAG)
+                .commitAllowingStateLoss();
+    }
+
+    public static void endCircularReveal(FragmentManager fm) {
+        final Fragment fragment = fm.findFragmentByTag(TAG);
+        if (fragment != null) {
+            fm.beginTransaction().remove(fragment).commitAllowingStateLoss();
+        }
+    }
+
+    /**
+     * Empty constructor used only by the {@link FragmentManager}.
+     */
+    public CircularRevealFragment() {}
+
+    public CircularRevealFragment(Point touchPoint, OnCircularRevealCompleteListener listener) {
+        mTouchPoint = touchPoint;
+        mListener = listener;
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        if (!mAnimationStarted) {
+            // Only run the animation once for each instance of the fragment
+            startOutgoingAnimation(InCallPresenter.getInstance().getThemeColors());
+        }
+        mAnimationStarted = true;
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.outgoing_call_animation, container, false);
+    }
+
+    public void startOutgoingAnimation(MaterialPalette palette) {
+        final Activity activity = getActivity();
+        if (activity == null) {
+            Log.w(this, "Asked to do outgoing call animation when not attached");
+            return;
+        }
+
+        final View view  = activity.getWindow().getDecorView();
+
+        // The circle starts from an initial size of 0 so clip it such that it is invisible.
+        // Otherwise the first frame is drawn with a fully opaque screen which causes jank. When
+        // the animation later starts, this clip will be clobbered by the circular reveal clip.
+        // See ViewAnimationUtils.createCircularReveal.
+        view.setOutlineProvider(new ViewOutlineProvider() {
+            @Override
+            public void getOutline(View view, Outline outline) {
+                // Using (0, 0, 0, 0) will not work since the outline will simply be treated as
+                // an empty outline.
+                outline.setOval(-1, -1, 0, 0);
+            }
+        });
+        view.setClipToOutline(true);
+
+        if (palette != null) {
+            view.findViewById(R.id.outgoing_call_animation_circle).setBackgroundColor(
+                    palette.mPrimaryColor);
+            activity.getWindow().setStatusBarColor(palette.mSecondaryColor);
+        }
+
+        view.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
+            @Override
+            public boolean onPreDraw() {
+                final ViewTreeObserver vto = view.getViewTreeObserver();
+                if (vto.isAlive()) {
+                    vto.removeOnPreDrawListener(this);
+                }
+                final Animator animator = getRevealAnimator(mTouchPoint);
+                animator.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        view.setClipToOutline(false);
+                        if (mListener != null) {
+                            mListener.onCircularRevealComplete(getFragmentManager());
+                        }
+                    }
+                });
+                animator.start();
+                return false;
+            }
+        });
+    }
+
+    private Animator getRevealAnimator(Point touchPoint) {
+        final Activity activity = getActivity();
+        final View view  = activity.getWindow().getDecorView();
+        final Display display = activity.getWindowManager().getDefaultDisplay();
+        final Point size = new Point();
+        display.getSize(size);
+
+        int startX = size.x / 2;
+        int startY = size.y / 2;
+        if (touchPoint != null) {
+            startX = touchPoint.x;
+            startY = touchPoint.y;
+        }
+
+        final Animator valueAnimator = ViewAnimationUtils.createCircularReveal(view,
+                startX, startY, 0, Math.max(size.x, size.y));
+        valueAnimator.setDuration(getResources().getInteger(R.integer.reveal_animation_duration));
+        return valueAnimator;
+    }
+}
diff --git a/InCallUI/src/com/android/incallui/InCallActivity.java b/InCallUI/src/com/android/incallui/InCallActivity.java
index 6c58213..9dc703a 100644
--- a/InCallUI/src/com/android/incallui/InCallActivity.java
+++ b/InCallUI/src/com/android/incallui/InCallActivity.java
@@ -66,7 +66,6 @@
     public static final String SHOW_DIALPAD_EXTRA = "InCallActivity.show_dialpad";
     public static final String DIALPAD_TEXT_EXTRA = "InCallActivity.dialpad_text";
     public static final String NEW_OUTGOING_CALL_EXTRA = "InCallActivity.new_outgoing_call";
-    public static final String SHOW_CIRCULAR_REVEAL_EXTRA = "InCallActivity.show_circular_reveal";
 
     private CallButtonFragment mCallButtonFragment;
     private CallCardFragment mCallCardFragment;
@@ -489,12 +488,9 @@
                     }
                 }
 
-                // This is only true in the case where an outgoing call is initiated by tapping
-                // on the "Select account dialog", in which case we skip the initial animation. In
-                // most other cases the circular reveal is done by OutgoingCallAnimationActivity.
-                final boolean showCircularReveal =
-                        intent.getBooleanExtra(SHOW_CIRCULAR_REVEAL_EXTRA, false);
-                mCallCardFragment.animateForNewOutgoingCall(touchPoint, showCircularReveal);
+                // Start animation for new outgoing call
+                CircularRevealFragment.startCircularReveal(getFragmentManager(), touchPoint,
+                        InCallPresenter.getInstance());
 
                 // InCallActivity is responsible for disconnecting a new outgoing call if there
                 // is no way of making it (i.e. no valid call capable accounts)
diff --git a/InCallUI/src/com/android/incallui/InCallPresenter.java b/InCallUI/src/com/android/incallui/InCallPresenter.java
index 318d869..0343ed6 100644
--- a/InCallUI/src/com/android/incallui/InCallPresenter.java
+++ b/InCallUI/src/com/android/incallui/InCallPresenter.java
@@ -16,14 +16,13 @@
 
 package com.android.incallui;
 
-import android.app.Activity;
+import android.app.FragmentManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.graphics.Point;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.Handler;
 import android.telecom.DisconnectCause;
 import android.telecom.PhoneAccount;
 import android.telecom.Phone;
@@ -57,7 +56,8 @@
  * that want to listen in on the in-call state changes.
  * TODO: This class has become more of a state machine at this point.  Consider renaming.
  */
-public class InCallPresenter implements CallList.Listener, InCallPhoneListener {
+public class InCallPresenter implements CallList.Listener, InCallPhoneListener,
+        CircularRevealFragment.OnCircularRevealCompleteListener {
 
     private static final String EXTRA_FIRST_TIME_SHOWN =
             "com.android.incallui.intent.extra.FIRST_TIME_SHOWN";
@@ -97,6 +97,16 @@
     private boolean mAccountSelectionCancelled = false;
     private InCallCameraManager mInCallCameraManager = null;
 
+    /**
+     * Whether or not we are currently bound and waiting for Telecom to send us a new call.
+     */
+    private boolean mBoundAndWaitingForOutgoingCall;
+    /**
+     * If there is no actual call currently in the call list, this will be used as a fallback
+     * to determine the theme color for InCallUI.
+     */
+    private PhoneAccountHandle mPendingPhoneAccountHandle;
+
     private final Phone.Listener mPhoneListener = new Phone.Listener() {
         @Override
         public void onBringToForeground(Phone phone, boolean showDialpad) {
@@ -105,6 +115,9 @@
         }
         @Override
         public void onCallAdded(Phone phone, android.telecom.Call call) {
+            // Since a call has been added we are no longer waiting for Telecom to send us a
+            // call.
+            setBoundAndWaitingForOutgoingCall(false, null);
             call.addListener(mCallListener);
         }
         @Override
@@ -155,20 +168,6 @@
      */
     private boolean mIsActivityPreviouslyStarted = false;
 
-
-    /**
-     * Whether or not to wait for the circular reveal animation to be started, to avoid stopping
-     * the circular reveal animation activity before the animation is initiated.
-     */
-    private boolean mWaitForRevealAnimationStart = false;
-
-    /**
-     * Whether or not the CircularRevealAnimationActivity has started.
-     */
-    private boolean mCircularRevealActivityStarted = false;
-
-    private boolean mShowDialpadOnStart = false;
-
     /**
      * Whether or not InCallService is bound to Telecom.
      */
@@ -176,8 +175,6 @@
 
     private Phone mPhone;
 
-    private Handler mHandler = new Handler();
-
     /** Display colors for the UI. Consists of a primary color and secondary (darker) color */
     private MaterialPalette mThemeColors;
 
@@ -260,13 +257,6 @@
     }
 
     private void attemptFinishActivity() {
-        mWaitForRevealAnimationStart = false;
-
-        Context context = mContext != null ? mContext : mInCallActivity;
-        if (context != null) {
-            CircularRevealActivity.sendClearDisplayBroadcast(context);
-        }
-
         final boolean doFinish = (mInCallActivity != null && isActivityStarted());
         Log.i(this, "Hide in call UI: " + doFinish);
         if (doFinish) {
@@ -455,7 +445,7 @@
     /**
      * Given the call list, return the state in which the in-call screen should be.
      */
-    public static InCallState getPotentialStateFromCallList(CallList callList) {
+    public InCallState getPotentialStateFromCallList(CallList callList) {
 
         InCallState newState = InCallState.NO_CALLS;
 
@@ -477,9 +467,40 @@
             newState = InCallState.INCALL;
         }
 
+        if (newState == InCallState.NO_CALLS) {
+            if (mBoundAndWaitingForOutgoingCall) {
+                return InCallState.OUTGOING;
+            }
+        }
+
         return newState;
     }
 
+    public boolean isBoundAndWaitingForOutgoingCall() {
+        return mBoundAndWaitingForOutgoingCall;
+    }
+
+    public void setBoundAndWaitingForOutgoingCall(boolean isBound, PhoneAccountHandle handle) {
+        // NOTE: It is possible for there to be a race and have handle become null before
+        // the circular reveal starts. This should not cause any problems because CallCardFragment
+        // should fallback to the actual call in the CallList at that point in time to determine
+        // the theme color.
+        Log.i(this, "setBoundAndWaitingForOutgoingCall: " + isBound);
+        mBoundAndWaitingForOutgoingCall = isBound;
+        mPendingPhoneAccountHandle = handle;
+        if (isBound && mInCallState == InCallState.NO_CALLS) {
+            mInCallState = InCallState.OUTGOING;
+        }
+    }
+
+    @Override
+    public void onCircularRevealComplete(FragmentManager fm) {
+        if (mInCallActivity != null) {
+            mInCallActivity.getCallCardFragment().animateForNewOutgoingCall();
+            CircularRevealFragment.endCircularReveal(mInCallActivity.getFragmentManager());
+        }
+    }
+
     public void addIncomingCallListener(IncomingCallListener listener) {
         Preconditions.checkNotNull(listener);
         mIncomingCallListeners.add(listener);
@@ -712,8 +733,6 @@
 
         if (showing) {
             mIsActivityPreviouslyStarted = true;
-        } else {
-            CircularRevealActivity.sendClearDisplayBroadcast(mContext);
         }
 
         for (InCallUiListener listener : mInCallUiListeners) {
@@ -1114,35 +1133,8 @@
     }
 
     public void showInCall(final boolean showDialpad, final boolean newOutgoingCall) {
-        if (mCircularRevealActivityStarted) {
-            mWaitForRevealAnimationStart = true;
-            mShowDialpadOnStart = showDialpad;
-            Log.i(this, "Waiting for circular reveal completion to show InCallActivity");
-        } else {
-            Log.i(this, "Showing InCallActivity immediately");
-            mContext.startActivity(getInCallIntent(showDialpad, newOutgoingCall,
-                    newOutgoingCall /* showCircularReveal */));
-        }
-    }
-
-    public void onCircularRevealStarted(final Activity activity) {
-        mCircularRevealActivityStarted = false;
-        if (mWaitForRevealAnimationStart) {
-            mWaitForRevealAnimationStart = false;
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    Log.i(this, "Showing InCallActivity after circular reveal");
-                    final Intent intent =
-                            getInCallIntent(mShowDialpadOnStart, true, false, false);
-                    activity.startActivity(intent);
-                    mShowDialpadOnStart = false;
-                }
-            });
-        } else if (!mServiceBound) {
-            CircularRevealActivity.sendClearDisplayBroadcast(mContext);
-            return;
-        }
+        Log.i(this, "Showing InCallActivity");
+        mContext.startActivity(getInCallIntent(showDialpad, newOutgoingCall));
     }
 
     public void onServiceBind() {
@@ -1150,6 +1142,7 @@
     }
 
     public void onServiceUnbind() {
+        InCallPresenter.getInstance().setBoundAndWaitingForOutgoingCall(false, null);
         mServiceBound = false;
     }
 
@@ -1174,43 +1167,26 @@
 
         final PhoneAccountHandle accountHandle =
                 intent.getParcelableExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
-        final MaterialPalette colors = getColorsFromPhoneAccountHandle(accountHandle);
         final Point touchPoint = extras.getParcelable(TouchPointManager.TOUCH_POINT);
 
-        mCircularRevealActivityStarted = true;
-        mContext.startActivity(getAnimationIntent(touchPoint, colors));
+        InCallPresenter.getInstance().setBoundAndWaitingForOutgoingCall(true, accountHandle);
+
+        final Intent incallIntent = getInCallIntent(false, true);
+        incallIntent.putExtra(TouchPointManager.TOUCH_POINT, touchPoint);
+        mContext.startActivity(incallIntent);
     }
 
-    private Intent getAnimationIntent(Point touchPoint, MaterialPalette palette) {
-        final Intent intent = new Intent(mContext, CircularRevealActivity.class);
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
-                | Intent.FLAG_ACTIVITY_NO_USER_ACTION);
-        intent.putExtra(TouchPointManager.TOUCH_POINT, touchPoint);
-        intent.putExtra(CircularRevealActivity.EXTRA_THEME_COLORS, palette);
-        return intent;
-    }
-
-    public Intent getInCallIntent(boolean showDialpad, boolean newOutgoingCall,
-            boolean showCircularReveal) {
-        return getInCallIntent(showDialpad, newOutgoingCall, showCircularReveal, true);
-    }
-
-    public Intent getInCallIntent(boolean showDialpad, boolean newOutgoingCall,
-            boolean showCircularReveal, boolean newTask) {
+    public Intent getInCallIntent(boolean showDialpad, boolean newOutgoingCall) {
         final Intent intent = new Intent(Intent.ACTION_MAIN, null);
         intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
-                | Intent.FLAG_ACTIVITY_NO_USER_ACTION);
-        if (newTask) {
-            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        }
+                | Intent.FLAG_ACTIVITY_NO_USER_ACTION
+                | Intent.FLAG_ACTIVITY_NEW_TASK);
 
         intent.setClass(mContext, InCallActivity.class);
         if (showDialpad) {
             intent.putExtra(InCallActivity.SHOW_DIALPAD_EXTRA, true);
         }
         intent.putExtra(InCallActivity.NEW_OUTGOING_CALL_EXTRA, newOutgoingCall);
-        intent.putExtra(InCallActivity.SHOW_CIRCULAR_REVEAL_EXTRA, showCircularReveal);
         return intent;
     }
 
@@ -1348,7 +1324,11 @@
     }
 
     private MaterialPalette getColorsFromCall(Call call) {
-        return getColorsFromPhoneAccountHandle(call == null ? null : call.getAccountHandle());
+        if (call == null) {
+            return getColorsFromPhoneAccountHandle(mPendingPhoneAccountHandle);
+        } else {
+            return getColorsFromPhoneAccountHandle(call.getAccountHandle());
+        }
     }
 
     private MaterialPalette getColorsFromPhoneAccountHandle(PhoneAccountHandle phoneAccountHandle) {
diff --git a/InCallUI/src/com/android/incallui/StatusBarNotifier.java b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
index 99392d9..89192f8 100644
--- a/InCallUI/src/com/android/incallui/StatusBarNotifier.java
+++ b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
@@ -627,8 +627,7 @@
     private PendingIntent createLaunchPendingIntent() {
 
         final Intent intent = InCallPresenter.getInstance().getInCallIntent(
-                false /* showDialpad */, false /* newOutgoingCall */,
-                        false /* showCircularReveal */);
+                false /* showDialpad */, false /* newOutgoingCall */);
 
         // PendingIntent that can be used to launch the InCallActivity.  The
         // system fires off this intent if the user pulls down the windowshade