From 654df8fbbc7703c24aec687de902e2c72ba343dd Mon Sep 17 00:00:00 2001 From: Yorke Lee Date: Mon, 12 May 2014 16:43:36 -0700 Subject: Animate call log expand/collapse --- res/layout/call_log_list_item.xml | 2 +- src/com/android/dialer/calllog/CallLogAdapter.java | 21 +++++- .../android/dialer/calllog/CallLogFragment.java | 85 +++++++++++++++++++++- .../dialer/calllog/CallLogListItemView.java | 8 -- src/com/android/dialer/list/ListsFragment.java | 2 +- src/com/android/dialerbind/ObjectFactory.java | 7 +- .../android/dialer/calllog/CallLogAdapterTest.java | 2 +- 7 files changed, 110 insertions(+), 17 deletions(-) diff --git a/res/layout/call_log_list_item.xml b/res/layout/call_log_list_item.xml index 63436b599..6068bd817 100644 --- a/res/layout/call_log_list_item.xml +++ b/res/layout/call_log_list_item.xml @@ -49,7 +49,6 @@ android:background="@drawable/call_log_background" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_centerVertical="true" android:padding="@dimen/call_log_outer_margin" android:orientation="horizontal" android:gravity="center_vertical" @@ -91,6 +90,7 @@ android:textSize="@dimen/call_log_secondary_text_size" android:singleLine="true" android:ellipsize="marquee" + android:visibility="gone" /> (); diff --git a/src/com/android/dialer/calllog/CallLogFragment.java b/src/com/android/dialer/calllog/CallLogFragment.java index 0f3e405c3..2b2d43c82 100644 --- a/src/com/android/dialer/calllog/CallLogFragment.java +++ b/src/com/android/dialer/calllog/CallLogFragment.java @@ -16,6 +16,9 @@ package com.android.dialer.calllog; +import android.animation.Animator; +import android.animation.ValueAnimator; +import android.animation.Animator.AnimatorListener; import android.app.Activity; import android.app.KeyguardManager; import android.app.ListFragment; @@ -30,9 +33,14 @@ import android.provider.CallLog; import android.provider.CallLog.Calls; import android.provider.ContactsContract; import android.provider.VoicemailContract.Status; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.view.ViewGroup.LayoutParams; +import android.widget.FrameLayout; +import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView; @@ -54,7 +62,9 @@ import java.util.List; * (all, missed or voicemails), specify it in the constructor. */ public class CallLogFragment extends ListFragment - implements CallLogQueryHandler.Listener, CallLogAdapter.CallFetcher { + implements CallLogQueryHandler.Listener, + CallLogAdapter.CallFetcher, + CallLogAdapter.CallItemExpandedListener { private static final String TAG = "CallLogFragment"; /** @@ -80,6 +90,8 @@ public class CallLogFragment extends ListFragment private boolean mCallLogFetched; private boolean mVoicemailStatusFetched; + private float mExpandedItemElevation; + private final Handler mHandler = new Handler(); private class CustomContentObserver extends ContentObserver { @@ -154,7 +166,7 @@ public class CallLogFragment extends ListFragment String currentCountryIso = GeoUtil.getCurrentCountryIso(getActivity()); mAdapter = ObjectFactory.newCallLogAdapter(getActivity(), this, new ContactInfoHelper( - getActivity(), currentCountryIso), true); + getActivity(), currentCountryIso), this, true); setListAdapter(mAdapter); mCallLogQueryHandler = new CallLogQueryHandler(getActivity().getContentResolver(), this, mLogLimit); @@ -168,6 +180,8 @@ public class CallLogFragment extends ListFragment Status.CONTENT_URI, true, mVoicemailStatusObserver); setHasOptionsMenu(true); updateCallList(mCallTypeFilter, mDateLimit); + + mExpandedItemElevation = getResources().getDimension(R.dimen.call_log_expanded_elevation); } /** Called by the CallLogQueryHandler when the list of calls has been fetched or updated. */ @@ -503,4 +517,71 @@ public class CallLogFragment extends ListFragment listView.removeFooterView(mFooterView); listView.addFooterView(mFooterView); } + + @Override + public void onItemExpanded(final CallLogListItemView view) { + final int startingHeight = view.getHeight(); + final CallLogListItemViews viewHolder = (CallLogListItemViews) view.getTag(); + final ViewTreeObserver observer = getListView().getViewTreeObserver(); + observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + // We don't want to continue getting called for every draw. + if (observer.isAlive()) { + observer.removeOnPreDrawListener(this); + } + // Calculate some values to help with the animation. + final int endingHeight = view.getHeight(); + final int distance = Math.abs(endingHeight - startingHeight); + final int baseHeight = Math.min(endingHeight, startingHeight); + final boolean isExpand = endingHeight > startingHeight; + + // Set the views back to the start state of the animation + view.getLayoutParams().height = startingHeight; + if (!isExpand) { + viewHolder.actionsView.setVisibility(View.VISIBLE); + } + view.requestLayout(); + + // Set up the animator to animate the expansion. + ValueAnimator animator = isExpand ? ValueAnimator.ofFloat(0f, 1f) + : ValueAnimator.ofFloat(1f, 0f); + + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animator) { + Float value = (Float) animator.getAnimatedValue(); + + // For each value from 0 to 1, animate the various parts of the layout. + view.getLayoutParams().height = + (int) (value * distance + baseHeight); + view.setElevation(mExpandedItemElevation * value); + view.requestLayout(); + } + }); + // Set everything to their final values when the animation's done. + animator.addListener(new AnimatorListener() { + @Override + public void onAnimationEnd(Animator animation) { + view.getLayoutParams().height = LayoutParams.WRAP_CONTENT; + if (!isExpand) { + viewHolder.actionsView.setVisibility(View.GONE); + } + } + + @Override + public void onAnimationCancel(Animator animation) {} + @Override + public void onAnimationRepeat(Animator animation) { } + @Override + public void onAnimationStart(Animator animation) { } + }); + animator.start(); + + // Return false so this draw does not occur to prevent the final frame from + // being drawn for the single frame before the animations start. + return false; + } + }); + } } diff --git a/src/com/android/dialer/calllog/CallLogListItemView.java b/src/com/android/dialer/calllog/CallLogListItemView.java index 113b02a5b..b8990f50a 100644 --- a/src/com/android/dialer/calllog/CallLogListItemView.java +++ b/src/com/android/dialer/calllog/CallLogListItemView.java @@ -35,12 +35,4 @@ public class CallLogListItemView extends LinearLayout { public CallLogListItemView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } - - @Override - public void requestLayout() { - // We will assume that once measured this will not need to resize - // itself, so there is no need to pass the layout request to the parent - // view (ListView). - forceLayout(); - } } diff --git a/src/com/android/dialer/list/ListsFragment.java b/src/com/android/dialer/list/ListsFragment.java index 4a26d4257..18fa531c0 100644 --- a/src/com/android/dialer/list/ListsFragment.java +++ b/src/com/android/dialer/list/ListsFragment.java @@ -214,7 +214,7 @@ public class ListsFragment extends Fragment implements CallLogQueryHandler.Liste this, 1); final String currentCountryIso = GeoUtil.getCurrentCountryIso(getActivity()); mCallLogAdapter = ObjectFactory.newCallLogAdapter(getActivity(), this, - new ContactInfoHelper(getActivity(), currentCountryIso), false); + new ContactInfoHelper(getActivity(), currentCountryIso), null, false); mMergedAdapter = new ShortcutCardsAdapter(getActivity(), this, mCallLogAdapter); } diff --git a/src/com/android/dialerbind/ObjectFactory.java b/src/com/android/dialerbind/ObjectFactory.java index e7ca8d9e3..97863116b 100644 --- a/src/com/android/dialerbind/ObjectFactory.java +++ b/src/com/android/dialerbind/ObjectFactory.java @@ -21,6 +21,7 @@ import static com.android.dialer.calllog.CallLogAdapter.CallFetcher; import android.content.Context; import com.android.dialer.calllog.CallLogAdapter; +import com.android.dialer.calllog.CallLogAdapter.CallItemExpandedListener; import com.android.dialer.calllog.ContactInfoHelper; import com.android.dialer.service.CachedNumberLookupService; @@ -43,7 +44,9 @@ public class ObjectFactory { * @return Instance of CallLogAdapter. */ public static CallLogAdapter newCallLogAdapter(Context context, CallFetcher callFetcher, - ContactInfoHelper contactInfoHelper, boolean isCallLog) { - return new CallLogAdapter(context, callFetcher, contactInfoHelper, isCallLog); + ContactInfoHelper contactInfoHelper, CallItemExpandedListener callItemExpandedListener, + boolean isCallLog) { + return new CallLogAdapter(context, callFetcher, contactInfoHelper, callItemExpandedListener, + isCallLog); } } diff --git a/tests/src/com/android/dialer/calllog/CallLogAdapterTest.java b/tests/src/com/android/dialer/calllog/CallLogAdapterTest.java index 55e422498..8225d1319 100644 --- a/tests/src/com/android/dialer/calllog/CallLogAdapterTest.java +++ b/tests/src/com/android/dialer/calllog/CallLogAdapterTest.java @@ -212,7 +212,7 @@ public class CallLogAdapterTest extends AndroidTestCase { public TestCallLogAdapter(Context context, CallFetcher callFetcher, ContactInfoHelper contactInfoHelper) { - super(context, callFetcher, contactInfoHelper, false); + super(context, callFetcher, contactInfoHelper, null, false); } @Override -- cgit v1.2.3