| /* |
| * Copyright (C) 2017 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.dialer.voicemail.listui; |
| |
| import static android.view.View.GONE; |
| import static android.view.View.VISIBLE; |
| |
| import android.app.FragmentManager; |
| import android.content.Context; |
| import android.database.Cursor; |
| import android.net.Uri; |
| import android.support.annotation.NonNull; |
| import android.support.v7.widget.RecyclerView; |
| import android.text.TextUtils; |
| import android.view.View; |
| import android.view.View.OnClickListener; |
| import android.widget.ImageView; |
| import android.widget.QuickContactBadge; |
| import android.widget.TextView; |
| import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.AnnotatedCallLog; |
| import com.android.dialer.common.Assert; |
| import com.android.dialer.common.LogUtil; |
| import com.android.dialer.contactphoto.ContactPhotoManager; |
| import com.android.dialer.lettertile.LetterTileDrawable; |
| import com.android.dialer.time.Clock; |
| import com.android.dialer.voicemail.listui.menu.NewVoicemailMenu; |
| import com.android.dialer.voicemail.model.VoicemailEntry; |
| |
| /** {@link RecyclerView.ViewHolder} for the new voicemail tab. */ |
| final class NewVoicemailViewHolder extends RecyclerView.ViewHolder implements OnClickListener { |
| |
| private final Context context; |
| private final TextView primaryTextView; |
| private final TextView secondaryTextView; |
| private final TextView transcriptionTextView; |
| private final QuickContactBadge quickContactBadge; |
| private final NewVoicemailMediaPlayerView mediaPlayerView; |
| private final ImageView menuButton; |
| private final Clock clock; |
| private boolean isViewHolderExpanded; |
| private int viewHolderId; |
| private VoicemailEntry voicemailEntryOfViewHolder; |
| @NonNull private Uri viewHolderVoicemailUri; |
| private final NewVoicemailViewHolderListener voicemailViewHolderListener; |
| |
| NewVoicemailViewHolder( |
| View view, Clock clock, NewVoicemailViewHolderListener newVoicemailViewHolderListener) { |
| super(view); |
| LogUtil.enterBlock("NewVoicemailViewHolder"); |
| this.context = view.getContext(); |
| primaryTextView = view.findViewById(R.id.primary_text); |
| secondaryTextView = view.findViewById(R.id.secondary_text); |
| transcriptionTextView = view.findViewById(R.id.transcription_text); |
| quickContactBadge = view.findViewById(R.id.quick_contact_photo); |
| mediaPlayerView = view.findViewById(R.id.new_voicemail_media_player); |
| menuButton = view.findViewById(R.id.menu_button); |
| this.clock = clock; |
| voicemailViewHolderListener = newVoicemailViewHolderListener; |
| |
| viewHolderId = -1; |
| isViewHolderExpanded = false; |
| viewHolderVoicemailUri = null; |
| } |
| |
| /** |
| * When the {@link RecyclerView} displays voicemail entries, it might recycle the views upon |
| * scrolling. In that case we need to ensure that the member variables of this {@link |
| * NewVoicemailViewHolder} and its views are correctly set, especially when this {@link |
| * NewVoicemailViewHolder} is recycled. |
| * |
| * @param cursor the voicemail data from {@link AnnotatedCallLog} generated by the {@link |
| * VoicemailCursorLoader} related |
| * @param fragmentManager FragmentManager retrieved from {@link |
| * NewVoicemailFragment#getActivity()} |
| * @param mediaPlayer |
| * @param position the position of the item within the adapter's data set. |
| * @param currentlyExpandedViewHolderId the value the adapter keeps track of which viewholder if |
| */ |
| void bindViewHolderValuesFromAdapter( |
| Cursor cursor, |
| FragmentManager fragmentManager, |
| NewVoicemailMediaPlayer mediaPlayer, |
| int position, |
| int currentlyExpandedViewHolderId) { |
| |
| LogUtil.i( |
| "NewVoicemailViewHolder.bindViewHolderValuesFromAdapter", |
| "view holder at pos:%d, adapterPos:%d, cursorPos:%d, cursorSize:%d", |
| position, |
| getAdapterPosition(), |
| cursor.getPosition(), |
| cursor.getCount()); |
| |
| voicemailEntryOfViewHolder = VoicemailCursorLoader.toVoicemailEntry(cursor); |
| viewHolderId = voicemailEntryOfViewHolder.id(); |
| LogUtil.i( |
| "NewVoicemailViewHolder.bindViewHolderValuesFromAdapter", "viewholderId:%d", viewHolderId); |
| viewHolderVoicemailUri = Uri.parse(voicemailEntryOfViewHolder.voicemailUri()); |
| primaryTextView.setText( |
| VoicemailEntryText.buildPrimaryVoicemailText(context, voicemailEntryOfViewHolder)); |
| secondaryTextView.setText( |
| VoicemailEntryText.buildSecondaryVoicemailText(context, clock, voicemailEntryOfViewHolder)); |
| |
| String voicemailTranscription = voicemailEntryOfViewHolder.transcription(); |
| |
| if (TextUtils.isEmpty(voicemailTranscription)) { |
| transcriptionTextView.setVisibility(GONE); |
| transcriptionTextView.setText(null); |
| } else { |
| transcriptionTextView.setVisibility(View.VISIBLE); |
| transcriptionTextView.setText(voicemailTranscription); |
| } |
| |
| itemView.setOnClickListener(this); |
| menuButton.setOnClickListener( |
| NewVoicemailMenu.createOnClickListener(context, voicemailEntryOfViewHolder)); |
| |
| setPhoto(voicemailEntryOfViewHolder); |
| |
| // Update the expanded/collapsed state of this view holder |
| // Only update the binding of the mediaPlayerView of the expanded view holder |
| if (viewHolderId == currentlyExpandedViewHolderId) { |
| LogUtil.i( |
| "NewVoicemailViewHolder.bindViewHolderValuesFromAdapter", |
| "viewHolderId:%d is expanded, update its mediaplayer view", |
| viewHolderId); |
| expandAndBindViewHolderAndMediaPlayerViewWithAdapterValues( |
| voicemailEntryOfViewHolder, fragmentManager, mediaPlayer, voicemailViewHolderListener); |
| LogUtil.i( |
| "NewVoicemailViewHolder.bindViewHolderValuesFromAdapter", |
| "After 2nd updating the MPPlayerView: viewHolderId:%d, uri:%s, MediaplayerView(after " |
| + "updated):%s, adapter position passed down:%d, getAdapterPos:%d", |
| viewHolderId, |
| String.valueOf(viewHolderVoicemailUri), |
| String.valueOf(mediaPlayerView.getVoicemailUri()), |
| position, |
| getAdapterPosition()); |
| Assert.checkArgument( |
| mediaPlayerView.getVisibility() == VISIBLE, |
| "a expanded viewholder should have its media player view visible"); |
| } else { |
| LogUtil.i( |
| "NewVoicemailViewHolder.bindViewHolderValuesFromAdapter", |
| "viewHolderId:%d is not the expanded one, collapse it and don't update the MpView", |
| viewHolderId); |
| collapseViewHolder(); |
| Assert.checkArgument( |
| mediaPlayerView.getVisibility() == GONE, |
| "a collapsed viewholder should not have its media player view visible"); |
| } |
| LogUtil.i( |
| "NewVoicemailViewHolder.bindViewHolderValuesFromAdapter", |
| "Final value after updating: viewHolderId:%d, uri:%s, MediaplayerView(not updated):%s," |
| + " adapter position passed down:%d, getAdapterPos:%d, MPPlayerVisibility:%b", |
| viewHolderId, |
| String.valueOf(viewHolderVoicemailUri), |
| String.valueOf(mediaPlayerView.getVoicemailUri()), |
| position, |
| getAdapterPosition(), |
| mediaPlayerView.getVisibility() == VISIBLE); |
| } |
| |
| // TODO(uabdullah): Consider/Implement TYPE (e.g Spam, TYPE_VOICEMAIL) |
| private void setPhoto(VoicemailEntry voicemailEntry) { |
| ContactPhotoManager.getInstance(context) |
| .loadDialerThumbnailOrPhoto( |
| quickContactBadge, |
| voicemailEntry.lookupUri() == null ? null : Uri.parse(voicemailEntry.lookupUri()), |
| voicemailEntry.photoId(), |
| voicemailEntry.photoUri() == null ? null : Uri.parse(voicemailEntry.photoUri()), |
| voicemailEntry.name(), |
| LetterTileDrawable.TYPE_DEFAULT); |
| } |
| |
| void collapseViewHolder() { |
| LogUtil.i( |
| "NewVoicemailViewHolder.collapseViewHolder", |
| "viewHolderId:%d is being collapsed, its MPViewUri:%s, its Uri is :%s", |
| viewHolderId, |
| String.valueOf(mediaPlayerView.getVoicemailUri()), |
| String.valueOf(viewHolderVoicemailUri)); |
| transcriptionTextView.setMaxLines(1); |
| isViewHolderExpanded = false; |
| |
| mediaPlayerView.reset(); |
| mediaPlayerView.setVisibility(GONE); |
| } |
| |
| // When we are recycling the views ensure that we reset the viewHolder, as if its brand new |
| public void reset() { |
| LogUtil.i( |
| "NewVoicemailViewHolder.reset()", |
| "Reset the viewholder, currently viewHolderId:%d, uri:%s, isViewHolderExpanded:%b, " |
| + "its MediaPlayerViewUri:%s", |
| viewHolderId, |
| String.valueOf(viewHolderVoicemailUri), |
| isViewHolderExpanded, |
| String.valueOf(mediaPlayerView.getVoicemailUri())); |
| |
| viewHolderId = -1; |
| isViewHolderExpanded = false; |
| viewHolderVoicemailUri = null; |
| |
| mediaPlayerView.reset(); |
| |
| LogUtil.i( |
| "NewVoicemailViewHolder.reset()", |
| "Reset the viewholder, after resetting viewHolderId:%d, uri:%s, isViewHolderExpanded:%b", |
| viewHolderId, |
| String.valueOf(viewHolderVoicemailUri), |
| isViewHolderExpanded); |
| } |
| |
| /** |
| * Is only called when a user either clicks a {@link NewVoicemailViewHolder} to expand it or if |
| * the user had already expanded, then scrolled the {@link NewVoicemailViewHolder} out of view and |
| * then scrolled it back into view, and during the binding (as the views are recyled in {@link |
| * RecyclerView}) we restore the expanded state of the {@link NewVoicemailViewHolder}. |
| * |
| * <p>This function also tracks if the state of this viewholder is expanded. |
| * |
| * @param voicemailEntry are the voicemail related values from the {@link AnnotatedCallLog} |
| * @param fragmentManager FragmentManager retrieved from {@link |
| * NewVoicemailFragment#getActivity()} |
| * @param mediaPlayer there should only be one instance of this passed down from the {@link |
| * NewVoicemailAdapter} |
| * @param voicemailViewHolderListener |
| */ |
| void expandAndBindViewHolderAndMediaPlayerViewWithAdapterValues( |
| VoicemailEntry voicemailEntry, |
| FragmentManager fragmentManager, |
| NewVoicemailMediaPlayer mediaPlayer, |
| NewVoicemailViewHolderListener voicemailViewHolderListener) { |
| |
| Assert.isNotNull(voicemailViewHolderListener); |
| Assert.checkArgument( |
| voicemailEntry.id() == viewHolderId, "ensure that the adapter binding has taken place"); |
| Assert.checkArgument( |
| Uri.parse(voicemailEntry.voicemailUri()).equals(viewHolderVoicemailUri), |
| "ensure that the adapter binding has taken place"); |
| LogUtil.i( |
| "NewVoicemailViewHolder.expandAndBindViewHolderAndMediaPlayerViewWithAdapterValues", |
| "voicemail id: %d, value of isViewHolderExpanded:%b, before setting it to be true, and" |
| + " value of ViewholderUri:%s, MPView:%s, before updating it", |
| viewHolderId, |
| isViewHolderExpanded, |
| String.valueOf(viewHolderVoicemailUri), |
| String.valueOf(mediaPlayerView.getVoicemailUri())); |
| |
| transcriptionTextView.setMaxLines(999); |
| isViewHolderExpanded = true; |
| // Once the media player is visible update its state |
| mediaPlayerView.setVisibility(View.VISIBLE); |
| mediaPlayerView.bindValuesFromAdapterOfExpandedViewHolderMediaPlayerView( |
| this, voicemailEntry, fragmentManager, mediaPlayer, voicemailViewHolderListener); |
| LogUtil.i( |
| "NewVoicemailViewHolder.expandAndBindViewHolderAndMediaPlayerViewWithAdapterValues", |
| "voicemail id: %d, value of isViewHolderExpanded:%b, after setting it to be true, and" |
| + " value of ViewholderUri:%s, MPView:%s, after updating it", |
| viewHolderId, |
| isViewHolderExpanded, |
| String.valueOf(viewHolderVoicemailUri), |
| String.valueOf(mediaPlayerView.getVoicemailUri())); |
| } |
| |
| /** |
| * Called when we want to update the voicemail that is currently playing Updates the Seekbar, |
| * duration timer and the play/pause button visibility when the expanded voicemail is being |
| * played. |
| */ |
| public void updateMediaPlayerViewWithPlayingState( |
| NewVoicemailViewHolder newVoicemailViewHolder, NewVoicemailMediaPlayer mp) { |
| |
| LogUtil.i( |
| "NewVoicemailViewHolder.updateMediaPlayerViewWithPlayingState", |
| "viewholderUri:%s, mediaPlayerViewUri:%s, MPPosition:%d, MpDuration:%d, MpIsPlaying:%b", |
| newVoicemailViewHolder.getViewHolderVoicemailUri().toString(), |
| mediaPlayerView.getVoicemailUri().toString(), |
| mp.getCurrentPosition(), |
| mp.getDuration(), |
| mp.isPlaying()); |
| |
| Assert.checkArgument( |
| mp.isPlaying(), |
| "this method is only called when we are certain that the media player is playing"); |
| |
| LogUtil.i( |
| "NewVoicemailViewHolder.updateMediaPlayerViewWithPlayingState", |
| "viewholderUri:%s, mediaPlayerViewUri:%s", |
| newVoicemailViewHolder.getViewHolderVoicemailUri().toString(), |
| mediaPlayerView.getVoicemailUri().toString()); |
| Assert.checkArgument( |
| newVoicemailViewHolder |
| .getViewHolderVoicemailUri() |
| .equals(mediaPlayerView.getVoicemailUri()), |
| "the mediaplayer view must be that of the viewholder we are updating"); |
| Assert.checkArgument( |
| mp.getLastPlayedOrPlayingVoicemailUri() |
| .equals(mp.getLastPreparedOrPreparingToPlayVoicemailUri()), |
| "the media player view we are attempting to update should be of the " |
| + "currently prepared and playing voicemail"); |
| |
| mediaPlayerView.updateSeekBarDurationAndShowPlayButton(mp); |
| } |
| |
| public void setMediaPlayerViewToResetState( |
| NewVoicemailViewHolder currentlyExpandedViewHolderOnScreen, |
| NewVoicemailMediaPlayer mediaPlayer) { |
| Assert.isNotNull(currentlyExpandedViewHolderOnScreen); |
| mediaPlayerView.setToResetState(currentlyExpandedViewHolderOnScreen, mediaPlayer); |
| } |
| |
| public void setPausedStateOfMediaPlayerView(Uri uri, NewVoicemailMediaPlayer mediaPlayer) { |
| Assert.checkArgument(viewHolderVoicemailUri.equals(uri)); |
| Assert.checkArgument(mediaPlayerView.getVoicemailUri().equals(uri)); |
| Assert.checkArgument(mediaPlayerView.getVoicemailUri().equals(viewHolderVoicemailUri)); |
| mediaPlayerView.setToPausedState(uri, mediaPlayer); |
| } |
| |
| boolean isViewHolderExpanded() { |
| return isViewHolderExpanded; |
| } |
| |
| public int getViewHolderId() { |
| return viewHolderId; |
| } |
| |
| public Uri getViewHolderVoicemailUri() { |
| return viewHolderVoicemailUri; |
| } |
| |
| public void clickPlayButtonOfViewHoldersMediaPlayerView( |
| NewVoicemailViewHolder expandedViewHolder) { |
| LogUtil.i( |
| "NewVoicemailViewHolder.clickPlayButtonOfViewHoldersMediaPlayerView", |
| "expandedViewHolderID:%d", |
| expandedViewHolder.getViewHolderId()); |
| |
| Assert.checkArgument( |
| mediaPlayerView.getVoicemailUri().equals(expandedViewHolder.getViewHolderVoicemailUri())); |
| Assert.checkArgument( |
| expandedViewHolder.getViewHolderVoicemailUri().equals(getViewHolderVoicemailUri())); |
| Assert.checkArgument( |
| mediaPlayerView.getVisibility() == View.VISIBLE, |
| "the media player must be visible for viewholder id:%d, before we attempt to play"); |
| mediaPlayerView.clickPlayButton(); |
| } |
| |
| interface NewVoicemailViewHolderListener { |
| void expandViewHolderFirstTimeAndCollapseAllOtherVisibleViewHolders( |
| NewVoicemailViewHolder expandedViewHolder, |
| VoicemailEntry voicemailEntryOfViewHolder, |
| NewVoicemailViewHolderListener listener); |
| |
| void collapseExpandedViewHolder(NewVoicemailViewHolder expandedViewHolder); |
| |
| void pauseViewHolder(NewVoicemailViewHolder expandedViewHolder); |
| |
| void resumePausedViewHolder(NewVoicemailViewHolder expandedViewHolder); |
| |
| void deleteViewHolder( |
| Context context, |
| FragmentManager fragmentManager, |
| NewVoicemailViewHolder expandedViewHolder, |
| Uri uri); |
| } |
| |
| @Override |
| public void onClick(View v) { |
| LogUtil.i( |
| "NewVoicemailViewHolder.onClick", |
| "voicemail id: %d, isViewHolderCurrentlyExpanded:%b", |
| viewHolderId, |
| isViewHolderExpanded); |
| if (isViewHolderExpanded) { |
| voicemailViewHolderListener.collapseExpandedViewHolder(this); |
| } else { |
| voicemailViewHolderListener.expandViewHolderFirstTimeAndCollapseAllOtherVisibleViewHolders( |
| this, |
| Assert.isNotNull(voicemailEntryOfViewHolder), |
| Assert.isNotNull(voicemailViewHolderListener)); |
| } |
| } |
| } |