Merge "Use blue pressed background state"
diff --git a/res/layout-xlarge/contact_browser.xml b/res/layout-xlarge/contact_browser.xml
index 39828c6..b03fc47 100644
--- a/res/layout-xlarge/contact_browser.xml
+++ b/res/layout-xlarge/contact_browser.xml
@@ -33,16 +33,27 @@
         ex:layout_wideWidth="430dip"
     />
 
-    <!--  Holder for detail- or editor-fragment. -->
-    <FrameLayout
-        android:id="@+id/detail_container"
+    <view
+        class="com.android.contacts.widget.TransitionAnimationView"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:paddingBottom="6dip"
         ex:layout_narrowParentWidth="800dip"
         ex:layout_narrowRightMargin="0dip"
         ex:layout_wideParentWidth="1280dip"
         ex:layout_wideRightMargin="48dip"
+        ex:clipMarginLeft="3dip"
+        ex:clipMarginTop="3dip"
+        ex:clipMarginRight="3dip"
+        ex:clipMarginBottom="9dip"
+        ex:enterAnimation="@android:anim/animator_fade_in"
+        ex:exitAnimation="@android:anim/animator_fade_out"
+        ex:animationDuration="80">
+        <FrameLayout
+            android:id="@+id/detail_container"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:paddingBottom="6dip"
         />
+    </view>
 
 </com.android.contacts.widget.InterpolatingLayout>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 0aff359..9bb9728 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -185,6 +185,16 @@
         <attr name="layout_wideRightMargin" format="dimension"/>
     </declare-styleable>
     
+    <declare-styleable name="TransitionAnimationView">
+        <attr name="clipMarginLeft" format="dimension"/>
+        <attr name="clipMarginRight" format="dimension"/>
+        <attr name="clipMarginTop" format="dimension"/>
+        <attr name="clipMarginBottom" format="dimension"/>
+        <attr name="enterAnimation" format="reference"/>
+        <attr name="exitAnimation" format="reference"/>
+        <attr name="animationDuration" format="integer"/>
+    </declare-styleable>
+    
     <style name="DirectoryHeader" parent="ContactBrowserTheme">
         <item name="android:background">@drawable/directory_bg</item>
     </style>
diff --git a/src/com/android/contacts/views/detail/ContactDetailFragment.java b/src/com/android/contacts/views/detail/ContactDetailFragment.java
index 91641ea..de00eff 100644
--- a/src/com/android/contacts/views/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/views/detail/ContactDetailFragment.java
@@ -35,6 +35,7 @@
 import com.android.contacts.views.ContactLoader;
 import com.android.contacts.views.GroupMetaData;
 import com.android.contacts.views.editor.SelectAccountDialogFragment;
+import com.android.contacts.widget.TransitionAnimationView;
 import com.android.internal.telephony.ITelephony;
 
 import android.accounts.Account;
@@ -98,7 +99,6 @@
 import android.widget.BaseAdapter;
 import android.widget.Button;
 import android.widget.ImageView;
-import android.widget.LinearLayout;
 import android.widget.ListView;
 import android.widget.TextView;
 import android.widget.Toast;
@@ -133,6 +133,7 @@
     private final ArrayList<Long> mWritableRawContactIds = new ArrayList<Long>();
     private int mNumPhoneNumbers = 0;
     private String mDefaultCountryIso;
+    private boolean mContactDataDisplayed;
 
     /**
      * Device capability: Set during buildEntries and used in the long-press context menu
@@ -172,6 +173,8 @@
     private ArrayList<ArrayList<ViewEntry>> mSections = new ArrayList<ArrayList<ViewEntry>>();
     private LayoutInflater mInflater;
 
+    private boolean mTransitionAnimationRequested;
+
     public ContactDetailFragment() {
         // Explicit constructor for inflation
 
@@ -264,6 +267,8 @@
         }
 
         mLookupUri = lookupUri;
+        mTransitionAnimationRequested = mContactDataDisplayed;
+        mContactDataDisplayed = true;
         if (mLookupUri == null) {
             mContactData = null;
             bindData();
@@ -283,6 +288,11 @@
             getActivity().invalidateOptionsMenu();
         }
 
+        if (mTransitionAnimationRequested) {
+            TransitionAnimationView.startAnimation(mView, mContactData == null);
+            mTransitionAnimationRequested = false;
+        }
+
         if (mContactData == null) {
             mView.setVisibility(View.INVISIBLE);
             return;
@@ -323,8 +333,6 @@
         }
 
         mView.setVisibility(View.VISIBLE);
-
-        getActivity().invalidateOptionsMenu();
     }
 
     /**
diff --git a/src/com/android/contacts/views/editor/ContactEditorFragment.java b/src/com/android/contacts/views/editor/ContactEditorFragment.java
index c4bf6c3..96158dd 100644
--- a/src/com/android/contacts/views/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/views/editor/ContactEditorFragment.java
@@ -1660,6 +1660,7 @@
             }
 
             mStatus = Status.EDITING;
+            mLookupUri = data.getLookupUri();
             final long setDataStartTime = SystemClock.elapsedRealtime();
             setData(data);
             final long setDataEndTime = SystemClock.elapsedRealtime();
diff --git a/src/com/android/contacts/widget/TransitionAnimationView.java b/src/com/android/contacts/widget/TransitionAnimationView.java
new file mode 100644
index 0000000..938ff8a
--- /dev/null
+++ b/src/com/android/contacts/widget/TransitionAnimationView.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2010 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.contacts.widget;
+
+import com.android.contacts.R;
+
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorInflater;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewParent;
+import android.widget.FrameLayout;
+
+/**
+ * A container for a view that needs to have exit/enter animations when rebinding data.
+ * This layout should have a single child.  Just before rebinding data that child
+ * should make this call:
+ * <pre>
+ *   TransitionAnimationView.startAnimation(this);
+ * </pre>
+ */
+public class TransitionAnimationView extends FrameLayout implements AnimatorListener {
+
+    private View mPreviousStateView;
+    private Bitmap mPreviousStateBitmap;
+    private int mEnterAnimationId;
+    private int mExitAnimationId;
+    private int mAnimationDuration;
+    private Rect mClipMargins = new Rect();
+    private Rect mClipRect = new Rect();
+    private Animator mEnterAnimation;
+    private Animator mExitAnimation;
+
+    public TransitionAnimationView(Context context) {
+        this(context, null, 0);
+    }
+
+    public TransitionAnimationView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public TransitionAnimationView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        TypedArray a = getContext().obtainStyledAttributes(
+                attrs, R.styleable.TransitionAnimationView);
+
+        mEnterAnimationId = a.getResourceId(R.styleable.TransitionAnimationView_enterAnimation,
+                android.R.anim.animator_fade_in);
+        mExitAnimationId = a.getResourceId(R.styleable.TransitionAnimationView_exitAnimation,
+                android.R.anim.animator_fade_out);
+        mClipMargins.left = a.getDimensionPixelOffset(
+                R.styleable.TransitionAnimationView_clipMarginLeft, 0);
+        mClipMargins.top = a.getDimensionPixelOffset(
+                R.styleable.TransitionAnimationView_clipMarginTop, 0);
+        mClipMargins.right = a.getDimensionPixelOffset(
+                R.styleable.TransitionAnimationView_clipMarginRight, 0);
+        mClipMargins.bottom = a.getDimensionPixelOffset(
+                R.styleable.TransitionAnimationView_clipMarginBottom, 0);
+        mAnimationDuration = a.getInt(
+                R.styleable.TransitionAnimationView_animationDuration, 100);
+
+        a.recycle();
+
+        mPreviousStateView = new View(context);
+        mPreviousStateView.setVisibility(View.INVISIBLE);
+        addView(mPreviousStateView);
+
+        mEnterAnimation = AnimatorInflater.loadAnimator(getContext(), mEnterAnimationId);
+        if (mEnterAnimation == null) {
+            throw new IllegalArgumentException("Invalid enter animation: " + mEnterAnimationId);
+        }
+        mEnterAnimation.addListener(this);
+
+        mExitAnimation = AnimatorInflater.loadAnimator(getContext(), mExitAnimationId);
+        if (mExitAnimation == null) {
+            throw new IllegalArgumentException("Invalid exit animation: " + mExitAnimationId);
+        }
+
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        if (changed) {
+            mPreviousStateBitmap = Bitmap.createBitmap(
+                    right - left, bottom - top, Bitmap.Config.ARGB_8888);
+            mPreviousStateView.setBackgroundDrawable(
+                    new BitmapDrawable(getContext().getResources(), mPreviousStateBitmap));
+            mClipRect.set(mClipMargins.left, mClipMargins.top,
+                    right - left - mClipMargins.right, bottom - top - mClipMargins.bottom);
+        }
+    }
+
+    public static void startAnimation(View view, boolean closing) {
+        TransitionAnimationView container = null;
+        ViewParent parent = view.getParent();
+        while (parent instanceof View) {
+            if (parent instanceof TransitionAnimationView) {
+                container = (TransitionAnimationView) parent;
+                break;
+            }
+            parent = parent.getParent();
+        }
+
+        if (container != null) {
+            container.start(view, closing);
+        }
+    }
+
+    private void start(View view, boolean closing) {
+        if (view.getVisibility() != View.VISIBLE) {
+            if (!closing) {
+                mEnterAnimation.setTarget(view);
+                mEnterAnimation.start();
+            }
+        } else if (closing) {
+            mExitAnimation.setTarget(view);
+            mExitAnimation.start();
+        } else {
+            Canvas canvas = new Canvas(mPreviousStateBitmap);
+            Paint paint = new Paint();
+            paint.setColor(Color.TRANSPARENT);
+            canvas.drawRect(0, 0, mPreviousStateBitmap.getWidth(), mPreviousStateBitmap.getHeight(),
+                    paint);
+            canvas.clipRect(mClipRect);
+            view.draw(canvas);
+            mPreviousStateView.setVisibility(View.VISIBLE);
+
+            mEnterAnimation.setTarget(view);
+            mEnterAnimation.setDuration(mAnimationDuration);
+            mEnterAnimation.start();
+        }
+    }
+
+    @Override
+    public void onAnimationCancel(Animator animation) {
+        mPreviousStateView.setVisibility(View.INVISIBLE);
+    }
+
+    @Override
+    public void onAnimationEnd(Animator animation) {
+        mPreviousStateView.setVisibility(View.INVISIBLE);
+    }
+
+    @Override
+    public void onAnimationStart(Animator animation) {
+    }
+
+    @Override
+    public void onAnimationRepeat(Animator animation) {
+    }
+}