Fixed contact detail animated transitions

When selecting different contacts on a tablet, the
animation is to set the contact details pane to white
and fade in the new contact details.

From quick contacts view (from Group or Favorites tab
and launcher screen shortcut), if quickcontact is opened
up and user touches the image, then when transitioning
back to the contact details, the contact details shows all
white and then transitions in the new contact details without
showing the previously selected contact details.

Bug: 6194409
Bug: 6324730
Change-Id: I5f56d46aef338b5d7a22e548548c42054656f381
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index bcceff4..0502d4d 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -560,6 +560,11 @@
                 case ContactsRequest.ACTION_VIEW_CONTACT:
                     // We redirect this intent to the detail activity on 1-pane, so we don't get
                     // here.  It's only for 2-pane.
+                    Uri currentlyLoadedContactUri = mContactDetailFragment.getUri();
+                    if (currentlyLoadedContactUri != null
+                            && !mRequest.getContactUri().equals(currentlyLoadedContactUri)) {
+                        mContactDetailsView.setMaskVisibility(true);
+                    }
                     tabToOpen = TabState.ALL;
                     break;
                 case ContactsRequest.ACTION_GROUP:
@@ -612,7 +617,7 @@
         // If we are switching from one group to another, do a cross-fade
         if (mGroupDetailFragment != null && mGroupDetailFragment.getGroupUri() != null &&
                 !UriUtils.areEqual(mGroupDetailFragment.getGroupUri(), groupUri)) {
-            mGroupDetailsView.startTransition(mGroupDetailFragment.getView(), false);
+            mGroupDetailsView.startMaskTransition(false);
         }
         mGroupDetailFragment.loadGroup(groupUri);
         invalidateOptionsMenuIfNeeded();
diff --git a/src/com/android/contacts/detail/ContactDetailFragment.java b/src/com/android/contacts/detail/ContactDetailFragment.java
index c91f493..5c68dcb 100644
--- a/src/com/android/contacts/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailFragment.java
@@ -356,6 +356,9 @@
         mShowStaticPhoto = showPhoto;
     }
 
+    /**
+     * Shows the contact detail with a message indicating there are no contact details.
+     */
     public void showEmptyState() {
         setData(null, null);
     }
@@ -406,6 +409,9 @@
 
         if (mContactData == null) {
             mView.setVisibility(View.INVISIBLE);
+            if (mStaticPhotoContainer != null) {
+                mStaticPhotoContainer.setVisibility(View.GONE);
+            }
             mAllEntries.clear();
             if (mAdapter != null) {
                 mAdapter.notifyDataSetChanged();
diff --git a/src/com/android/contacts/detail/ContactDetailLayoutController.java b/src/com/android/contacts/detail/ContactDetailLayoutController.java
index a99f35f..e6a5984 100644
--- a/src/com/android/contacts/detail/ContactDetailLayoutController.java
+++ b/src/com/android/contacts/detail/ContactDetailLayoutController.java
@@ -291,8 +291,7 @@
         if (PhoneCapabilityTester.isUsingTwoPanes(mActivity)) {
             // Tablet: If we already showed data before, we want to cross-fade from screen to screen
             if (contactWasLoaded && mTransitionAnimationView != null && isDifferentContact) {
-                mTransitionAnimationView.startTransition(
-                        mViewContainer, mContactData == null);
+                mTransitionAnimationView.startMaskTransition(mContactData == null);
             }
         } else {
             // Small screen: We are on our own screen. Fade the data in, but only the first time
@@ -367,7 +366,7 @@
                     // This is screen is very hard to animate properly, because there is such a hard
                     // cut from the regular version. A proper animation would have to reflow text
                     // and move things around. Doing a simple cross-fade instead.
-                    mTransitionAnimationView.startTransition(mViewContainer, false);
+                    mTransitionAnimationView.startMaskTransition(false);
                 }
 
                 // Set the contact data (hide the static photo because the photo will already be in
diff --git a/src/com/android/contacts/util/ImageViewDrawableSetter.java b/src/com/android/contacts/util/ImageViewDrawableSetter.java
index 5cde022..1d23dd0 100644
--- a/src/com/android/contacts/util/ImageViewDrawableSetter.java
+++ b/src/com/android/contacts/util/ImageViewDrawableSetter.java
@@ -39,10 +39,10 @@
     private ImageView mTarget;
     private byte[] mCompressed;
     private Drawable mPreviousDrawable;
+    private int mDurationInMillis = 0;
     private static final String TAG = "ImageViewDrawableSetter";
 
     public ImageViewDrawableSetter() {
-
     }
 
     public ImageViewDrawableSetter(ImageView target) {
@@ -54,6 +54,10 @@
         setCompressedImage(contactData.getPhotoBinaryData());
     }
 
+    public void setTransitionDuration(int durationInMillis) {
+        mDurationInMillis = durationInMillis;
+    }
+
     public ImageView getTarget() {
         return mTarget;
     }
@@ -97,7 +101,7 @@
         // If we don't have a new Drawable, something went wrong... bail out.
         if (newDrawable == null) return previousBitmap();
 
-        if (mPreviousDrawable == null) {
+        if (mPreviousDrawable == null || mDurationInMillis == 0) {
             // Set the new one immediately.
             mTarget.setImageDrawable(newDrawable);
         } else {
@@ -107,7 +111,7 @@
             beforeAndAfter[1] = newDrawable;
             final TransitionDrawable transition = new TransitionDrawable(beforeAndAfter);
             mTarget.setImageDrawable(transition);
-            transition.startTransition(200);
+            transition.startTransition(mDurationInMillis);
         }
 
         // Remember this for next time, so that we can transition from it to the
diff --git a/src/com/android/contacts/widget/TransitionAnimationView.java b/src/com/android/contacts/widget/TransitionAnimationView.java
index cefc82d..28d728b 100644
--- a/src/com/android/contacts/widget/TransitionAnimationView.java
+++ b/src/com/android/contacts/widget/TransitionAnimationView.java
@@ -15,30 +15,20 @@
  */
 package com.android.contacts.widget;
 
-import android.animation.Animator;
-import android.animation.Animator.AnimatorListener;
 import android.animation.ObjectAnimator;
 import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.drawable.BitmapDrawable;
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.FrameLayout;
 
 /**
- * A container for a view that needs to have exit/enter animations when rebinding data.
- * After rebinding the contents, the following call should be made (where child is the only visible)
- * child
- * <pre>
- *   TransitionAnimationView.startAnimation(child);
- * </pre>
+ * A container that places a masking view on top of all other views.  The masking view can be
+ * faded in and out.  Currently, the masking view is solid color white.
  */
-public class TransitionAnimationView extends FrameLayout implements AnimatorListener {
-    private View mPreviousStateView;
-    private Bitmap mPreviousStateBitmap;
-    private ObjectAnimator mPreviousAnimator;
+public class TransitionAnimationView extends FrameLayout {
+    private View mMaskingView;
+    private ObjectAnimator mAnimator;
 
     public TransitionAnimationView(Context context) {
         this(context, null, 0);
@@ -55,75 +45,43 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mPreviousStateView = new View(getContext());
-        mPreviousStateView.setVisibility(View.INVISIBLE);
-        mPreviousStateView.setLayoutParams(new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
+        mMaskingView = new View(getContext());
+        mMaskingView.setVisibility(View.INVISIBLE);
+        mMaskingView.setLayoutParams(new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
                 LayoutParams.MATCH_PARENT));
-        addView(mPreviousStateView);
+        mMaskingView.setBackgroundColor(Color.WHITE);
+        addView(mMaskingView);
     }
 
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        mPreviousStateView.setBackgroundDrawable(null);
-        if (mPreviousStateBitmap != null) {
-            mPreviousStateBitmap.recycle();
-            mPreviousStateBitmap = null;
-        }
-    }
-
-    public void startTransition(View view, boolean closing) {
-        if (mPreviousAnimator != null && mPreviousAnimator.isRunning()) {
-            mPreviousAnimator.end();
-        }
-        if (view.getVisibility() != View.VISIBLE) {
-            if (!closing) {
-                mPreviousAnimator = ObjectAnimator.ofFloat(view, View.ALPHA, 0.0f, 1.0f);
-                mPreviousAnimator.start();
-            }
-        } else if (closing) {
-            mPreviousAnimator = ObjectAnimator.ofFloat(view, View.ALPHA, 1.0f, 0.0f);
-            mPreviousAnimator.start();
+    public void setMaskVisibility(boolean flag) {
+        if (flag) {
+            mMaskingView.setAlpha(1.0f);
+            mMaskingView.setVisibility(View.VISIBLE);
         } else {
-            if (view.getWidth() > 0 && view.getHeight() > 0) {
-                // Take a "screenshot" of the current state of the screen and show that on top
-                // of the real content. Then, fade that out.
-                mPreviousStateBitmap = Bitmap.createBitmap(
-                        view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
-                mPreviousStateView.setBackgroundDrawable(
-                        new BitmapDrawable(getContext().getResources(), mPreviousStateBitmap));
-                mPreviousStateView.setLayoutParams(view.getLayoutParams());
-                mPreviousStateBitmap.eraseColor(Color.WHITE);
-                Canvas canvas = new Canvas(mPreviousStateBitmap);
-                view.draw(canvas);
-                canvas.setBitmap(null);
-                mPreviousStateView.setVisibility(View.VISIBLE);
-
-                mPreviousAnimator =
-                        ObjectAnimator.ofFloat(mPreviousStateView, View.ALPHA, 1.0f, 0.0f);
-                mPreviousAnimator.start();
-            }
+            mMaskingView.setVisibility(View.INVISIBLE);
         }
     }
 
-    @Override
-    public void onAnimationEnd(Animator animation) {
-        mPreviousStateView.setVisibility(View.INVISIBLE);
-        mPreviousStateView.setBackgroundDrawable(null);
-        mPreviousStateBitmap.recycle();
-        mPreviousStateBitmap = null;
-        mPreviousAnimator = null;
-    }
+    /**
+     * Starts the transition of showing or hiding the mask.
+     * If showMask is true, the mask will be set to be invisible then fade into hide the other
+     * views in this container.  If showMask is false, the mask will be set to be hide other views
+     * initially.  Then, the other views in this container will be revealed.
+     */
+    public void startMaskTransition(boolean showMask) {
+        // Stop any animation that may still be running.
+        if (mAnimator != null && mAnimator.isRunning()) {
+            mAnimator.end();
+        }
 
-    @Override
-    public void onAnimationCancel(Animator animation) {
-    }
-
-    @Override
-    public void onAnimationStart(Animator animation) {
-    }
-
-    @Override
-    public void onAnimationRepeat(Animator animation) {
+        mMaskingView.setVisibility(View.VISIBLE);
+        if (showMask) {
+            mAnimator = ObjectAnimator.ofFloat(mMaskingView, View.ALPHA, 0.0f, 1.0f);
+            mAnimator.start();
+        } else {
+            // asked to hide the view
+            mAnimator = ObjectAnimator.ofFloat(mMaskingView, View.ALPHA, 1.0f, 0.0f);
+            mAnimator.start();
+        }
     }
 }