Add play voicemail primary action to call log.

+ Add voicemail primary action button, which expands the call log
and plays immediately when clicked.
+ Pass expand/collapse listener into the view holder. This is
necessary because it needs to be triggered when the "play" primary
action is clicked so that the CallLogAdapter correctly registers
what has been added and binded.
+ Update primary action button state when showing or hiding actions,
so the visibility of the voicemail play button is managed properly.
+ Ensure voicemail playback state is consistent between multiple
call log items when the user initiates a collapse or expand. Add
reset function to help manage this.
+ With the reset, protect against the possibility of functions in
the presenter being called when no voicemail playback view is set.

Bug: 21654755
Change-Id: I7bcf67d27fa08fe77d1334dc084b52effe8d3ccc
diff --git a/res/drawable-hdpi/ic_play_arrow_24dp.png b/res/drawable-hdpi/ic_play_arrow_24dp.png
new file mode 100644
index 0000000..57c9fa5
--- /dev/null
+++ b/res/drawable-hdpi/ic_play_arrow_24dp.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_play_arrow_24dp.png b/res/drawable-mdpi/ic_play_arrow_24dp.png
new file mode 100644
index 0000000..c61e948
--- /dev/null
+++ b/res/drawable-mdpi/ic_play_arrow_24dp.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_play_arrow_24dp.png b/res/drawable-xhdpi/ic_play_arrow_24dp.png
new file mode 100644
index 0000000..a3c80e7
--- /dev/null
+++ b/res/drawable-xhdpi/ic_play_arrow_24dp.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_play_arrow_24dp.png b/res/drawable-xxhdpi/ic_play_arrow_24dp.png
new file mode 100644
index 0000000..547ef30
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_play_arrow_24dp.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_play_arrow_24dp.png b/res/drawable-xxxhdpi/ic_play_arrow_24dp.png
new file mode 100644
index 0000000..be5c062
--- /dev/null
+++ b/res/drawable-xxxhdpi/ic_play_arrow_24dp.png
Binary files differ
diff --git a/src/com/android/dialer/calllog/CallLogAdapter.java b/src/com/android/dialer/calllog/CallLogAdapter.java
index 3c7e080..4304002 100644
--- a/src/com/android/dialer/calllog/CallLogAdapter.java
+++ b/src/com/android/dialer/calllog/CallLogAdapter.java
@@ -116,9 +116,13 @@
                 return;
             }
 
+            // Always reset the voicemail playback state on expand or collapse.
+            mVoicemailPlaybackPresenter.reset();
+
             if (viewHolder.getAdapterPosition() == mCurrentlyExpandedPosition) {
                 // Hide actions, if the clicked item is the expanded item.
                 viewHolder.showActions(false);
+
                 mCurrentlyExpandedPosition = RecyclerView.NO_POSITION;
                 mCurrentlyExpandedRowId = NO_EXPANDED_LIST_ITEM;
             } else {
@@ -289,6 +293,7 @@
         CallLogListItemViewHolder viewHolder = CallLogListItemViewHolder.create(
                 view,
                 mContext,
+                mExpandCollapseListener,
                 mPhoneNumberUtilsWrapper,
                 mCallLogViewsHelper,
                 mVoicemailPlaybackPresenter);
@@ -297,7 +302,6 @@
         viewHolder.callLogEntryView.setAccessibilityDelegate(mAccessibilityDelegate);
 
         viewHolder.primaryActionView.setTag(viewHolder);
-        viewHolder.primaryActionView.setOnClickListener(mExpandCollapseListener);
 
         return viewHolder;
     }
@@ -402,7 +406,6 @@
             mCurrentlyExpandedPosition = position;
         }
         views.showActions(mCurrentlyExpandedPosition == position);
-        views.updatePrimaryActionButton();
 
         String nameForDefaultImage = null;
         if (TextUtils.isEmpty(info.name)) {
diff --git a/src/com/android/dialer/calllog/CallLogListItemViewHolder.java b/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
index 1d6a434..f54720b 100644
--- a/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
+++ b/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
@@ -145,8 +145,12 @@
 
     private final int mPhotoSize;
 
+    private View.OnClickListener mExpandCollapseListener;
+    private boolean mVoicemailPrimaryActionButtonClicked;
+
     private CallLogListItemViewHolder(
             Context context,
+            View.OnClickListener expandCollapseListener,
             PhoneNumberUtilsWrapper phoneNumberUtilsWrapper,
             CallLogListItemHelper callLogListItemHelper,
             VoicemailPlaybackPresenter voicemailPlaybackPresenter,
@@ -160,6 +164,7 @@
         super(rootView);
 
         mContext = context;
+        mExpandCollapseListener = expandCollapseListener;
         mPhoneNumberUtilsWrapper = phoneNumberUtilsWrapper;
         mCallLogListItemHelper = callLogListItemHelper;
         mVoicemailPlaybackPresenter = voicemailPlaybackPresenter;
@@ -182,17 +187,20 @@
         quickContactView.setPrioritizedMimeType(Phone.CONTENT_ITEM_TYPE);
 
         primaryActionButtonView.setOnClickListener(this);
+        primaryActionView.setOnClickListener(mExpandCollapseListener);
     }
 
     public static CallLogListItemViewHolder create(
             View view,
             Context context,
+            View.OnClickListener expandCollapseListener,
             PhoneNumberUtilsWrapper phoneNumberUtilsWrapper,
             CallLogListItemHelper callLogListItemHelper,
             VoicemailPlaybackPresenter voicemailPlaybackPresenter) {
 
         return new CallLogListItemViewHolder(
                 context,
+                expandCollapseListener,
                 phoneNumberUtilsWrapper,
                 callLogListItemHelper,
                 voicemailPlaybackPresenter,
@@ -243,8 +251,17 @@
         bindActionButtons();
     }
 
-    public void updatePrimaryActionButton() {
-        if (TextUtils.isEmpty(voicemailUri)) {
+    private void updatePrimaryActionButton(boolean isExpanded) {
+        if (!TextUtils.isEmpty(voicemailUri)) {
+            // Treat as voicemail list item; show play button if not expanded.
+            if (!isExpanded) {
+                primaryActionButtonView.setImageResource(R.drawable.ic_play_arrow_24dp);
+                primaryActionButtonView.setVisibility(View.VISIBLE);
+            } else {
+                primaryActionButtonView.setVisibility(View.GONE);
+            }
+        } else {
+            // Treat as normal list item; show call button, if possible.
             boolean canPlaceCallToNumber =
                     PhoneNumberUtilsWrapper.canPlaceCallsTo(number, numberPresentation);
 
@@ -306,7 +323,8 @@
 
             Uri uri = Uri.parse(voicemailUri);
             mVoicemailPlaybackPresenter.setPlaybackView(
-                    voicemailPlaybackView, uri, false /* startPlayingImmediately */);
+                    voicemailPlaybackView, uri, mVoicemailPrimaryActionButtonClicked);
+            mVoicemailPrimaryActionButtonClicked = false;
 
             CallLogAsyncTaskUtil.markVoicemailAsRead(mContext, uri);
         } else {
@@ -356,6 +374,8 @@
                 actionsView.setVisibility(View.GONE);
             }
         }
+
+        updatePrimaryActionButton(show);
     }
 
     public void expandVoicemailTranscriptionView(boolean isExpanded) {
@@ -402,12 +422,17 @@
 
     @Override
     public void onClick(View view) {
-        final IntentProvider intentProvider = (IntentProvider) view.getTag();
-        if (intentProvider != null) {
-            final Intent intent = intentProvider.getIntent(mContext);
-            // See IntentProvider.getCallDetailIntentProvider() for why this may be null.
-            if (intent != null) {
-                DialerUtils.startActivityWithErrorToast(mContext, intent);
+        if (view.getId() == R.id.primary_action_button && !TextUtils.isEmpty(voicemailUri)) {
+            mVoicemailPrimaryActionButtonClicked = true;
+            mExpandCollapseListener.onClick(primaryActionView);
+        } else {
+            final IntentProvider intentProvider = (IntentProvider) view.getTag();
+            if (intentProvider != null) {
+                final Intent intent = intentProvider.getIntent(mContext);
+                // See IntentProvider.getCallDetailIntentProvider() for why this may be null.
+                if (intent != null) {
+                    DialerUtils.startActivityWithErrorToast(mContext, intent);
+                }
             }
         }
     }
@@ -421,6 +446,7 @@
 
         CallLogListItemViewHolder viewHolder = new CallLogListItemViewHolder(
                 context,
+                null /* expandCollapseListener */,
                 phoneNumberUtilsWrapper,
                 new CallLogListItemHelper(phoneCallDetailsHelper, resources),
                 null /* voicemailPlaybackPresenter */,
diff --git a/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java b/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
index d47e9e2..cc64376 100644
--- a/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
+++ b/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
@@ -178,6 +178,18 @@
         mMediaPlayer.setOnCompletionListener(this);
     }
 
+    public void reset() {
+        pausePlayback();
+
+        mView = null;
+        mVoicemailUri = null;
+
+        mIsPrepared = false;
+        mIsPlaying = false;
+        mPosition = 0;
+        mDuration.set(0);
+    }
+
     /**
      * Specify the view which this presenter controls and the voicemail for playback.
      */
@@ -204,7 +216,6 @@
             mView.onSpeakerphoneOn(false);
 
             checkForContent();
-
         }
     }
 
@@ -341,7 +352,9 @@
         public void run() {
             if (mIsWaitingForResult.getAndSet(false)) {
                 mContext.getContentResolver().unregisterContentObserver(this);
-                mView.setFetchContentTimeout();
+                if (mView != null) {
+                    mView.setFetchContentTimeout();
+                }
             }
         }
 
@@ -384,7 +397,11 @@
      * and it will call {@link #onError()} otherwise.
      */
     private void prepareToPlayContent() {
+        if (mView == null) {
+            return;
+        }
         mIsPrepared = false;
+
         mView.setIsBuffering();
 
         try {
@@ -402,7 +419,11 @@
      */
     @Override
     public void onPrepared(MediaPlayer mp) {
+        if (mView == null) {
+            return;
+        }
         mIsPrepared = true;
+
         mDuration.set(mMediaPlayer.getDuration());
 
         mView.enableUiElements();
@@ -421,7 +442,7 @@
      */
     @Override
     public boolean onError(MediaPlayer mp, int what, int extra) {
-        handleError(new IllegalStateException("MediaPlayer error listener invoked"));
+        handleError(new IllegalStateException("MediaPlayer error listener invoked: " + extra));
         return true;
     }
 
diff --git a/tests/src/com/android/dialer/calllog/CallLogFragmentTest.java b/tests/src/com/android/dialer/calllog/CallLogFragmentTest.java
index b3906f8..6f4b68b 100644
--- a/tests/src/com/android/dialer/calllog/CallLogFragmentTest.java
+++ b/tests/src/com/android/dialer/calllog/CallLogFragmentTest.java
@@ -147,7 +147,7 @@
      */
     @MediumTest
     public void testCallViewIsNotVisibleForPrivateAndUnknownNumbers() {
-        final int SIZE = 100;
+        final int SIZE = 50;
         mList = new CallLogListItemViewHolder[SIZE];
 
         // Insert the first batch of entries.
@@ -320,7 +320,6 @@
         CallLogListItemViewHolder viewHolder = (CallLogListItemViewHolder)
                 mAdapter.onCreateViewHolder(mParentView, /* viewType */ 0);
         bindViewForTest(viewHolder);
-        viewHolder.updatePrimaryActionButton();
 
         // The primaryActionView tag is set in the
         // {@link com.android.dialer.calllog.CallLogAdapter#bindView} method.  If it is possible
@@ -453,7 +452,6 @@
             }
         });
         getInstrumentation().waitForIdleSync();
-        viewHolder.updatePrimaryActionButton();
     }
 
     private void bindViewForTest(CallLogListItemViewHolder viewHolder) {