Merge "Reduce jank in QuickContactActivity dismissal."
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 2132dc3..f11449c 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -121,7 +121,6 @@
         <item name="android:windowContentOverlay">@null</item>
         <item name="android:windowAnimationStyle">@null</item>
         <item name="android:windowIsFloating">false</item>
-        <item name="android:backgroundDimEnabled">true</item>
         <item name="android:windowIsTranslucent">true</item>
         <item name="android:windowNoTitle">true</item>
         <item name="android:listViewStyle">@style/ListViewStyle</item>
diff --git a/src/com/android/contacts/quickcontact/FloatingChildLayout.java b/src/com/android/contacts/quickcontact/FloatingChildLayout.java
index 75141ae..6302c5d 100644
--- a/src/com/android/contacts/quickcontact/FloatingChildLayout.java
+++ b/src/com/android/contacts/quickcontact/FloatingChildLayout.java
@@ -24,7 +24,11 @@
 import android.content.res.Resources;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.TransitionDrawable;
+import android.os.Handler;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewPropertyAnimator;
@@ -46,11 +50,16 @@
  * {@link PopupWindow} might work better.
  */
 public class FloatingChildLayout extends FrameLayout {
-    private static final String TAG = "FloatingChild";
+    private static final String TAG = "FloatingChildLayout";
     private int mFixedTopPosition;
     private View mChild;
+    private boolean mIsShowingChild;
     private Rect mTargetScreen = new Rect();
     private final int mAnimationDuration;
+    private final TransitionDrawable mBackground;
+
+    // Black, 50% alpha as per the system default.
+    private static final int DIM_BACKGROUND_COLOR = 0x7F000000;
 
     public FloatingChildLayout(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -58,6 +67,11 @@
         mFixedTopPosition =
                 resources.getDimensionPixelOffset(R.dimen.quick_contact_top_position);
         mAnimationDuration = resources.getInteger(android.R.integer.config_shortAnimTime);
+
+        final ColorDrawable[] drawables =
+            { new ColorDrawable(0), new ColorDrawable(DIM_BACKGROUND_COLOR) };
+        mBackground = new TransitionDrawable(drawables);
+        super.setBackground(mBackground);
     }
 
     @Override
@@ -69,6 +83,8 @@
         mChild.setScaleX(0.5f);
         mChild.setScaleY(0.5f);
         mChild.setAlpha(0.0f);
+
+        mIsShowingChild = false;
     }
 
     public View getChild() {
@@ -76,6 +92,14 @@
     }
 
     /**
+     * FloatingChildLayout manages its own background, don't set it.
+     */
+    @Override
+    public void setBackground(Drawable background) {
+        Log.wtf(TAG, "don't setBackground(), it is managed internally");
+    }
+
+    /**
      * Set {@link Rect} in screen coordinates that {@link #getChild()} should be
      * centered around.
      */
@@ -140,15 +164,40 @@
     }
 
     /** Begin animating {@link #getChild()} visible. */
-    public void showChild(Runnable onAnimationEndRunnable) {
+    public void showChild(final Runnable onAnimationEndRunnable) {
+        if (mIsShowingChild) return;
+        mIsShowingChild = true;
+
+        // TODO: understand this.
+        // For some reason this needs wait a tick in order to avoid jank.
+        // Maybe because we set up a hardware layer in animateScale()?
+        // Probably not, since it should also be required in hideChild().
+        new Handler().post(new Runnable() {
+            @Override public void run() {
+                animateBackground(false);
+            }
+        });
+
         animateScale(false, onAnimationEndRunnable);
     }
 
     /** Begin animating {@link #getChild()} invisible. */
-    public void hideChild(Runnable onAnimationEndRunnable) {
+    public void hideChild(final Runnable onAnimationEndRunnable) {
+        if (!mIsShowingChild) return;
+        mIsShowingChild = false;
+
+        animateBackground(true);
         animateScale(true, onAnimationEndRunnable);
     }
 
+    private void animateBackground(boolean isExitAnimation) {
+        if (isExitAnimation) {
+            mBackground.reverseTransition(mAnimationDuration);
+        } else {
+            mBackground.startTransition(mAnimationDuration);
+        }
+    }
+
     /** Creates the open/close animation */
     private void animateScale(boolean isExitAnimation, final Runnable onAnimationEndRunnable) {
         mChild.setPivotX(mTargetScreen.centerX() - mChild.getLeft());
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
index 92e68d4..e00f051 100644
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -18,7 +18,6 @@
 
 import com.android.contacts.Collapser;
 import com.android.contacts.ContactLoader;
-import com.android.contacts.ContactPhotoManager;
 import com.android.contacts.R;
 import com.android.contacts.model.AccountTypeManager;
 import com.android.contacts.model.DataKind;
@@ -41,7 +40,6 @@
 import android.content.Intent;
 import android.content.Loader;
 import android.content.pm.PackageManager;
-import android.graphics.BitmapFactory;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
@@ -91,6 +89,7 @@
 
     private static final boolean TRACE_LAUNCH = false;
     private static final String TRACE_TAG = "quickcontact";
+    private static final int POST_DRAW_WAIT_DURATION = 60;
 
     @SuppressWarnings("deprecation")
     private static final String LEGACY_AUTHORITY = android.provider.Contacts.AUTHORITY;
@@ -242,7 +241,25 @@
             mFloatingLayout.hideChild(new Runnable() {
                 @Override
                 public void run() {
-                    finish();
+                    // Wait until the final animation frame has been drawn, otherwise
+                    // there is jank as the framework transitions to the next Activity.
+                    SchedulingUtils.doAfterDraw(mFloatingLayout, new Runnable() {
+                        @Override
+                        public void run() {
+                            // Unfortunately, we need to also use postDelayed() to wait a moment
+                            // for the frame to be drawn, else the framework's activity-transition
+                            // animation will kick in before the final frame is available to it.
+                            // This seems unavoidable.  The problem isn't merely that there is no
+                            // post-draw listener API; if that were so, it would be sufficient to
+                            // call post() instead of postDelayed().
+                            new Handler().postDelayed(new Runnable() {
+                                @Override
+                                public void run() {
+                                    finish();
+                                }
+                            }, POST_DRAW_WAIT_DURATION);
+                        }
+                    });
                 }
             });
         } else {
diff --git a/src/com/android/contacts/util/SchedulingUtils.java b/src/com/android/contacts/util/SchedulingUtils.java
index 71fad05..c55bc16 100644
--- a/src/com/android/contacts/util/SchedulingUtils.java
+++ b/src/com/android/contacts/util/SchedulingUtils.java
@@ -18,10 +18,12 @@
 
 import android.view.View;
 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.view.ViewTreeObserver.OnDrawListener;
 
 /** Static methods that are useful for scheduling actions to occur at a later time. */
 public class SchedulingUtils {
 
+
     /** Runs a piece of code after the next layout run */
     public static void doAfterLayout(final View view, final Runnable runnable) {
         final OnGlobalLayoutListener listener = new OnGlobalLayoutListener() {
@@ -34,4 +36,16 @@
         };
         view.getViewTreeObserver().addOnGlobalLayoutListener(listener);
     }
+
+    /** Runs a piece of code just before the next draw. */
+    public static void doAfterDraw(final View view, final Runnable runnable) {
+        final OnDrawListener listener = new OnDrawListener() {
+            @Override
+            public void onDraw() {
+                view.getViewTreeObserver().removeOnDrawListener(this);
+                runnable.run();
+            }
+        };
+        view.getViewTreeObserver().addOnDrawListener(listener);
+    }
 }