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 --- 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 +- 5 files changed, 108 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/com/android/dialer/calllog/CallLogAdapter.java b/src/com/android/dialer/calllog/CallLogAdapter.java index 77e6d00d8..0aca9136b 100644 --- a/src/com/android/dialer/calllog/CallLogAdapter.java +++ b/src/com/android/dialer/calllog/CallLogAdapter.java @@ -66,6 +66,15 @@ public class CallLogAdapter extends GroupingListAdapter REMOVE_CALL_LOG_ENTRIES, } + /** Interface used to inform a parent UI element that a list item has been expanded. */ + public interface CallItemExpandedListener { + /** + * @param view The {@link CallLogListItemView} that represents the item that was clicked + * on. + */ + public void onItemExpanded(CallLogListItemView view); + } + /** Interface used to initiate a refresh of the content. */ public interface CallFetcher { public void fetchCalls(); @@ -199,6 +208,8 @@ public class CallLogAdapter extends GroupingListAdapter /** Helper to group call log entries. */ private final CallLogGroupBuilder mCallLogGroupBuilder; + private CallItemExpandedListener mCallItemExpandedListener; + /** Can be set to true by tests to disable processing of requests. */ private volatile boolean mRequestProcessingDisabled = false; @@ -245,7 +256,7 @@ public class CallLogAdapter extends GroupingListAdapter private final View.OnClickListener mExpandCollapseListener = new View.OnClickListener() { @Override public void onClick(View v) { - final View callLogItem = (View) v.getParent().getParent(); + final CallLogListItemView callLogItem = (CallLogListItemView) v.getParent().getParent(); final CallLogListItemViews views = (CallLogListItemViews) callLogItem.getTag(); // Hide or show the actions view. @@ -253,6 +264,11 @@ public class CallLogAdapter extends GroupingListAdapter // Trigger loading of the viewstub and visual expand or collapse. expandOrCollapseActions(callLogItem, expanded); + + if (mCallItemExpandedListener != null) { + mCallItemExpandedListener.onItemExpanded(callLogItem); + } + notifyDataSetChanged(); } }; @@ -297,7 +313,7 @@ public class CallLogAdapter extends GroupingListAdapter }; public CallLogAdapter(Context context, CallFetcher callFetcher, - ContactInfoHelper contactInfoHelper, + ContactInfoHelper contactInfoHelper, CallItemExpandedListener callItemExpandedListener, boolean isCallLog) { super(context); @@ -305,6 +321,7 @@ public class CallLogAdapter extends GroupingListAdapter mCallFetcher = callFetcher; mContactInfoHelper = contactInfoHelper; mIsCallLog = isCallLog; + mCallItemExpandedListener = callItemExpandedListener; mContactInfoCache = ExpirableCache.create(CONTACT_INFO_CACHE_SIZE); mRequests = new LinkedList(); 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); } } -- cgit v1.2.3