Merge "Fixed various issues around cross-fades"
diff --git a/res/layout-sw580dp/contact_detail_container.xml b/res/layout-sw580dp/contact_detail_container.xml
index 6ddc98d..fc09dfb 100644
--- a/res/layout-sw580dp/contact_detail_container.xml
+++ b/res/layout-sw580dp/contact_detail_container.xml
@@ -16,10 +16,7 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingLeft="16dip"
- android:paddingTop="16dip"
- android:paddingRight="16dip">
+ android:layout_height="match_parent">
<android.support.v4.view.ViewPager
android:id="@+id/pager"
diff --git a/res/layout-sw580dp/people_activity.xml b/res/layout-sw580dp/people_activity.xml
index 80ede2d..f93e55e 100644
--- a/res/layout-sw580dp/people_activity.xml
+++ b/res/layout-sw580dp/people_activity.xml
@@ -53,18 +53,11 @@
<view
class="com.android.contacts.widget.TransitionAnimationView"
- android:id="@+id/details_view"
+ android:id="@+id/contact_details_view"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@color/background_primary"
- ex:clipMarginLeft="0dip"
- ex:clipMarginTop="3dip"
- ex:clipMarginRight="3dip"
- ex:clipMarginBottom="9dip"
- ex:enterAnimation="@android:animator/fade_in"
- ex:exitAnimation="@android:animator/fade_out"
- ex:animationDuration="200"
android:visibility="gone">
<!-- This layout includes all possible views needed for a contact detail page -->
@@ -72,7 +65,10 @@
android:id="@+id/contact_detail_container"
layout="@layout/contact_detail_container"
android:layout_width="match_parent"
- android:layout_height="match_parent" />
+ android:layout_height="match_parent"
+ android:layout_marginLeft="16dip"
+ android:layout_marginTop="16dip"
+ android:layout_marginRight="16dip" />
<!-- This invisible worker fragment loads the contact's details -->
<fragment
@@ -81,6 +77,16 @@
android:layout_height="0dip"
android:layout_width="0dip"
android:visibility="gone"/>
+ </view>
+
+ <view
+ class="com.android.contacts.widget.TransitionAnimationView"
+ android:id="@+id/group_details_view"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:background="@color/background_primary"
+ android:visibility="gone">
<!-- This is the group detail page -->
<fragment
diff --git a/res/layout-sw680dp-w1000dp/people_activity.xml b/res/layout-sw680dp-w1000dp/people_activity.xml
index f45324e..2dea4eb 100644
--- a/res/layout-sw680dp-w1000dp/people_activity.xml
+++ b/res/layout-sw680dp-w1000dp/people_activity.xml
@@ -55,7 +55,7 @@
<view
class="com.android.contacts.widget.TransitionAnimationView"
- android:id="@+id/details_view"
+ android:id="@+id/contact_details_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
ex:layout_narrowParentWidth="800dip"
@@ -64,13 +64,6 @@
ex:layout_wideParentWidth="1280dip"
ex:layout_wideMarginLeft="0dip"
ex:layout_wideMarginRight="0dip"
- ex:clipMarginLeft="0dip"
- ex:clipMarginTop="3dip"
- ex:clipMarginRight="3dip"
- ex:clipMarginBottom="9dip"
- ex:enterAnimation="@android:animator/fade_in"
- ex:exitAnimation="@android:animator/fade_out"
- ex:animationDuration="200"
android:visibility="gone">
<!-- This layout includes all possible views needed for a contact detail page -->
@@ -87,6 +80,20 @@
android:layout_height="0dip"
android:layout_width="0dip"
android:visibility="gone"/>
+ </view>
+
+ <view
+ class="com.android.contacts.widget.TransitionAnimationView"
+ android:id="@+id/group_details_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ ex:layout_narrowParentWidth="800dip"
+ ex:layout_narrowMarginLeft="0dip"
+ ex:layout_narrowMarginRight="0dip"
+ ex:layout_wideParentWidth="1280dip"
+ ex:layout_wideMarginLeft="0dip"
+ ex:layout_wideMarginRight="0dip"
+ android:visibility="gone">
<!-- This is the group detail page -->
<fragment
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index b9a534c..4402d05 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -56,16 +56,6 @@
<attr name="ratio" format="float"/>
</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>
-
<declare-styleable name="ContactBrowser">
<attr name="contact_browser_list_padding_left" format="dimension"/>
<attr name="contact_browser_list_padding_right" format="dimension"/>
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index e41adad..0d226d8 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -56,6 +56,7 @@
import com.android.contacts.util.AccountFilterUtil;
import com.android.contacts.util.AccountPromptUtils;
import com.android.contacts.util.AccountsListAdapter;
+import com.android.contacts.util.UriUtils;
import com.android.contacts.util.AccountsListAdapter.AccountListFilter;
import com.android.contacts.util.Constants;
import com.android.contacts.util.DialogManager;
@@ -160,7 +161,8 @@
private View mFavoritesView;
private View mBrowserView;
- private TransitionAnimationView mDetailsView;
+ private TransitionAnimationView mContactDetailsView;
+ private TransitionAnimationView mGroupDetailsView;
private View mAddGroupImageView;
@@ -393,7 +395,8 @@
// Container views for fragments
mFavoritesView = getView(R.id.favorites_view);
- mDetailsView = getView(R.id.details_view);
+ mContactDetailsView = getView(R.id.contact_details_view);
+ mGroupDetailsView = getView(R.id.group_details_view);
mBrowserView = getView(R.id.browse_view);
// 2-pane only fragments
@@ -416,7 +419,8 @@
// Configure contact details
mContactDetailLayoutController = new ContactDetailLayoutController(this, savedState,
- getFragmentManager(), mDetailsView, findViewById(R.id.contact_detail_container),
+ getFragmentManager(), mContactDetailsView,
+ findViewById(R.id.contact_detail_container),
new ContactDetailFragmentListener());
}
transaction.commitAllowingStateLoss();
@@ -592,6 +596,11 @@
}
private void setupGroupDetailFragment(Uri groupUri) {
+ // 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);
+ }
mGroupDetailFragment.loadGroup(groupUri);
invalidateOptionsMenuIfNeeded();
}
@@ -664,18 +673,21 @@
case FAVORITES:
mFavoritesView.setVisibility(View.VISIBLE);
mBrowserView.setVisibility(View.GONE);
- mDetailsView.setVisibility(View.GONE);
+ mGroupDetailsView.setVisibility(View.GONE);
+ mContactDetailsView.setVisibility(View.GONE);
break;
case GROUPS:
mFavoritesView.setVisibility(View.GONE);
mBrowserView.setVisibility(View.VISIBLE);
- mDetailsView.setVisibility(View.VISIBLE);
+ mGroupDetailsView.setVisibility(View.VISIBLE);
+ mContactDetailsView.setVisibility(View.GONE);
mGroupsFragment.setAddAccountsVisibility(!areGroupWritableAccountsAvailable());
break;
case ALL:
mFavoritesView.setVisibility(View.GONE);
mBrowserView.setVisibility(View.VISIBLE);
- mDetailsView.setVisibility(View.VISIBLE);
+ mContactDetailsView.setVisibility(View.VISIBLE);
+ mGroupDetailsView.setVisibility(View.GONE);
break;
}
FragmentManager fragmentManager = getFragmentManager();
diff --git a/src/com/android/contacts/detail/ContactDetailLayoutController.java b/src/com/android/contacts/detail/ContactDetailLayoutController.java
index b79ccc0..a87f97e 100644
--- a/src/com/android/contacts/detail/ContactDetailLayoutController.java
+++ b/src/com/android/contacts/detail/ContactDetailLayoutController.java
@@ -250,20 +250,25 @@
public void setContactData(ContactLoader.Result data) {
final boolean contactWasLoaded;
final boolean contactHadUpdates;
+ final boolean isDifferentContact;
if (mContactData == null) {
contactHadUpdates = false;
contactWasLoaded = false;
+ isDifferentContact = true;
} else {
contactHadUpdates = mContactHasUpdates;
contactWasLoaded = true;
+ isDifferentContact =
+ !UriUtils.areEqual(mContactData.getLookupUri(), data.getLookupUri());
}
mContactData = data;
mContactHasUpdates = !data.getStreamItems().isEmpty();
if (PhoneCapabilityTester.isUsingTwoPanes(mActivity)) {
// Tablet: If we already showed data before, we want to cross-fade from screen to screen
- if (contactWasLoaded && mTransitionAnimationView != null) {
- mTransitionAnimationView.startTransition(mViewContainer, mContactData == null);
+ if (contactWasLoaded && mTransitionAnimationView != null && isDifferentContact) {
+ mTransitionAnimationView.startTransition(
+ mViewContainer, mContactData == null);
}
} else {
// Small screen: We are on our own screen. Fade the data in, but only the first time
@@ -326,9 +331,12 @@
switch (mLayoutMode) {
case TWO_COLUMN: {
- // 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. No animation for now
+ if (!isDifferentContact && animateStateChange) {
+ // 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);
+ }
// Set the contact data (hide the static photo because the photo will already be in
// the header that scrolls with contact details).
diff --git a/src/com/android/contacts/group/GroupDetailFragment.java b/src/com/android/contacts/group/GroupDetailFragment.java
index 3a43e66..b4f642a 100644
--- a/src/com/android/contacts/group/GroupDetailFragment.java
+++ b/src/com/android/contacts/group/GroupDetailFragment.java
@@ -191,6 +191,10 @@
mShowGroupActionInActionBar = show;
}
+ public Uri getGroupUri() {
+ return mGroupUri;
+ }
+
/**
* Start the loader to retrieve the metadata for this group.
*/
diff --git a/src/com/android/contacts/widget/TransitionAnimationView.java b/src/com/android/contacts/widget/TransitionAnimationView.java
index c70ca25..cefc82d 100644
--- a/src/com/android/contacts/widget/TransitionAnimationView.java
+++ b/src/com/android/contacts/widget/TransitionAnimationView.java
@@ -15,17 +15,13 @@
*/
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.animation.ObjectAnimator;
import android.content.Context;
-import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.view.View;
@@ -33,23 +29,16 @@
/**
* 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:
+ * After rebinding the contents, the following call should be made (where child is the only visible)
+ * child
* <pre>
- * TransitionAnimationView.startAnimation(this);
+ * TransitionAnimationView.startAnimation(child);
* </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;
+ private ObjectAnimator mPreviousAnimator;
public TransitionAnimationView(Context context) {
this(context, null, 0);
@@ -61,67 +50,16 @@
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.animator.fade_in);
- mExitAnimationId = a.getResourceId(R.styleable.TransitionAnimationView_exitAnimation,
- android.R.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);
- mEnterAnimation.setDuration(mAnimationDuration);
-
- mExitAnimation = AnimatorInflater.loadAnimator(getContext(), mExitAnimationId);
- if (mExitAnimation == null) {
- throw new IllegalArgumentException("Invalid exit animation: " + mExitAnimationId);
- }
- mExitAnimation.setDuration(mAnimationDuration);
}
@Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- if (changed || mPreviousStateBitmap == null) {
- if (mPreviousStateBitmap != null) {
- mPreviousStateBitmap.recycle();
- mPreviousStateBitmap = null;
- }
- int width = right - left;
- int height = bottom - top;
- if (width > 0 && height > 0) {
- mPreviousStateBitmap = Bitmap.createBitmap(
- width, height, Bitmap.Config.ARGB_8888);
- mPreviousStateView.setBackgroundDrawable(
- new BitmapDrawable(getContext().getResources(), mPreviousStateBitmap));
- mClipRect.set(mClipMargins.left, mClipMargins.top,
- width - mClipMargins.right, height - mClipMargins.bottom);
- } else {
- mPreviousStateBitmap = null;
- mPreviousStateView.setBackgroundDrawable(null);
- }
- }
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mPreviousStateView = new View(getContext());
+ mPreviousStateView.setVisibility(View.INVISIBLE);
+ mPreviousStateView.setLayoutParams(new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
+ LayoutParams.MATCH_PARENT));
+ addView(mPreviousStateView);
}
@Override
@@ -135,40 +73,46 @@
}
public void startTransition(View view, boolean closing) {
- if (mEnterAnimation.isRunning()) {
- mEnterAnimation.end();
- }
- if (mExitAnimation.isRunning()) {
- mExitAnimation.end();
+ if (mPreviousAnimator != null && mPreviousAnimator.isRunning()) {
+ mPreviousAnimator.end();
}
if (view.getVisibility() != View.VISIBLE) {
if (!closing) {
- mEnterAnimation.setTarget(view);
- mEnterAnimation.start();
+ mPreviousAnimator = ObjectAnimator.ofFloat(view, View.ALPHA, 0.0f, 1.0f);
+ mPreviousAnimator.start();
}
} else if (closing) {
- mExitAnimation.setTarget(view);
- mExitAnimation.start();
+ mPreviousAnimator = ObjectAnimator.ofFloat(view, View.ALPHA, 1.0f, 0.0f);
+ mPreviousAnimator.start();
} else {
- if (mPreviousStateBitmap == null) {
- return;
+ 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();
}
-
- mPreviousStateBitmap.eraseColor(Color.TRANSPARENT);
- Canvas canvas = new Canvas(mPreviousStateBitmap);
- canvas.clipRect(mClipRect);
- view.draw(canvas);
- canvas.setBitmap(null);
- mPreviousStateView.setVisibility(View.VISIBLE);
-
- mEnterAnimation.setTarget(view);
- mEnterAnimation.start();
}
}
@Override
public void onAnimationEnd(Animator animation) {
mPreviousStateView.setVisibility(View.INVISIBLE);
+ mPreviousStateView.setBackgroundDrawable(null);
+ mPreviousStateBitmap.recycle();
+ mPreviousStateBitmap = null;
+ mPreviousAnimator = null;
}
@Override