diff options
Diffstat (limited to 'src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java')
-rw-r--r-- | src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java | 329 |
1 files changed, 291 insertions, 38 deletions
diff --git a/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java b/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java index c918d7944..d4d294e8d 100644 --- a/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java +++ b/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java @@ -16,35 +16,46 @@ package com.android.dialer.voicemail; -import android.app.Activity; -import android.app.Fragment; +import android.content.ContentUris; import android.content.Context; -import android.media.MediaPlayer; +import android.content.Intent; +import android.database.Cursor; +import android.graphics.drawable.Drawable; import android.net.Uri; -import android.os.Bundle; -import android.os.PowerManager; -import android.provider.VoicemailContract; +import android.os.AsyncTask; +import android.os.Handler; import android.util.AttributeSet; -import android.util.Log; +import android.support.design.widget.Snackbar; import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; +import android.widget.Space; import android.widget.TextView; +import android.widget.Toast; import com.android.common.io.MoreCloseables; +import com.android.dialer.PhoneCallDetails; import com.android.dialer.R; import com.android.dialer.calllog.CallLogAsyncTaskUtil; -import com.google.common.base.Preconditions; +import com.android.dialer.database.VoicemailArchiveContract; +import com.android.dialer.database.VoicemailArchiveContract.VoicemailArchive; +import com.android.dialer.util.AsyncTaskExecutor; +import com.android.dialer.util.AsyncTaskExecutors; +import com.android.dialerbind.ObjectFactory; +import com.google.common.annotations.VisibleForTesting; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledExecutorService; +import javax.annotation.Nullable; import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.NotThreadSafe; import javax.annotation.concurrent.ThreadSafe; @@ -58,8 +69,16 @@ import javax.annotation.concurrent.ThreadSafe; */ @NotThreadSafe public class VoicemailPlaybackLayout extends LinearLayout - implements VoicemailPlaybackPresenter.PlaybackView { + implements VoicemailPlaybackPresenter.PlaybackView, + CallLogAsyncTaskUtil.CallLogAsyncTaskListener { private static final String TAG = VoicemailPlaybackLayout.class.getSimpleName(); + private static final int VOICEMAIL_DELETE_DELAY_MS = 3000; + private static final int VOICEMAIL_ARCHIVE_DELAY_MS = 3000; + + /** The enumeration of {@link AsyncTask} objects we use in this class. */ + public enum Tasks { + QUERY_ARCHIVED_STATUS + } /** * Controls the animation of the playback slider. @@ -145,6 +164,11 @@ public class VoicemailPlaybackLayout extends LinearLayout @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { setClipPosition(progress, seekBar.getMax()); + // Update the seek position if user manually changed it. This makes sure position gets + // updated when user use volume button to seek playback in talkback mode. + if (fromUser) { + mPresenter.seek(progress); + } } }; @@ -155,7 +179,7 @@ public class VoicemailPlaybackLayout extends LinearLayout @Override public void onClick(View v) { if (mPresenter != null) { - onSpeakerphoneOn(!mPresenter.isSpeakerphoneOn()); + mPresenter.toggleSpeakerphone(); } } }; @@ -185,26 +209,96 @@ public class VoicemailPlaybackLayout extends LinearLayout return; } mPresenter.pausePlayback(); - CallLogAsyncTaskUtil.deleteVoicemail(mContext, mVoicemailUri, null); mPresenter.onVoicemailDeleted(); + + final Uri deleteUri = mVoicemailUri; + final Runnable deleteCallback = new Runnable() { + @Override + public void run() { + if (Objects.equals(deleteUri, mVoicemailUri)) { + CallLogAsyncTaskUtil.deleteVoicemail(mContext, deleteUri, + VoicemailPlaybackLayout.this); + } + } + }; + + final Handler handler = new Handler(); + // Add a little buffer time in case the user clicked "undo" at the end of the delay + // window. + handler.postDelayed(deleteCallback, VOICEMAIL_DELETE_DELAY_MS + 50); + + Snackbar.make(VoicemailPlaybackLayout.this, R.string.snackbar_voicemail_deleted, + Snackbar.LENGTH_LONG) + .setDuration(VOICEMAIL_DELETE_DELAY_MS) + .setAction(R.string.snackbar_voicemail_deleted_undo, + new View.OnClickListener() { + @Override + public void onClick(View view) { + mPresenter.onVoicemailDeleteUndo(); + handler.removeCallbacks(deleteCallback); + } + }) + .setActionTextColor( + mContext.getResources().getColor( + R.color.dialer_snackbar_action_text_color)) + .show(); + } + }; + + private final View.OnClickListener mArchiveButtonListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mPresenter == null || isArchiving(mVoicemailUri)) { + return; + } + mIsArchiving.add(mVoicemailUri); + mPresenter.pausePlayback(); + updateArchiveUI(mVoicemailUri); + disableUiElements(); + mPresenter.archiveContent(mVoicemailUri, true); + } + }; + + private final View.OnClickListener mShareButtonListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mPresenter == null || isArchiving(mVoicemailUri)) { + return; + } + disableUiElements(); + mPresenter.archiveContent(mVoicemailUri, false); } }; private Context mContext; private VoicemailPlaybackPresenter mPresenter; private Uri mVoicemailUri; - + private final AsyncTaskExecutor mAsyncTaskExecutor = + AsyncTaskExecutors.createAsyncTaskExecutor(); private boolean mIsPlaying = false; + /** + * Keeps track of which voicemails are currently being archived in order to update the voicemail + * card UI every time a user opens a new card. + */ + private static final ArrayList<Uri> mIsArchiving = new ArrayList<>(); private SeekBar mPlaybackSeek; private ImageButton mStartStopButton; private ImageButton mPlaybackSpeakerphone; private ImageButton mDeleteButton; + private ImageButton mArchiveButton; + private ImageButton mShareButton; + + private Space mArchiveSpace; + private Space mShareSpace; + private TextView mStateText; private TextView mPositionText; private TextView mTotalDurationText; private PositionUpdater mPositionUpdater; + private Drawable mVoicemailSeekHandleEnabled; + private Drawable mVoicemailSeekHandleDisabled; public VoicemailPlaybackLayout(Context context) { this(context, null); @@ -212,7 +306,6 @@ public class VoicemailPlaybackLayout extends LinearLayout public VoicemailPlaybackLayout(Context context, AttributeSet attrs) { super(context, attrs); - mContext = context; LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); @@ -223,6 +316,16 @@ public class VoicemailPlaybackLayout extends LinearLayout public void setPresenter(VoicemailPlaybackPresenter presenter, Uri voicemailUri) { mPresenter = presenter; mVoicemailUri = voicemailUri; + if (ObjectFactory.isVoicemailArchiveEnabled(mContext)) { + updateArchiveUI(mVoicemailUri); + updateArchiveButton(mVoicemailUri); + } + + if (ObjectFactory.isVoicemailShareEnabled(mContext)) { + // Show share button and space before it + mShareSpace.setVisibility(View.VISIBLE); + mShareButton.setVisibility(View.VISIBLE); + } } @Override @@ -233,6 +336,12 @@ public class VoicemailPlaybackLayout extends LinearLayout mStartStopButton = (ImageButton) findViewById(R.id.playback_start_stop); mPlaybackSpeakerphone = (ImageButton) findViewById(R.id.playback_speakerphone); mDeleteButton = (ImageButton) findViewById(R.id.delete_voicemail); + mArchiveButton =(ImageButton) findViewById(R.id.archive_voicemail); + mShareButton = (ImageButton) findViewById(R.id.share_voicemail); + + mArchiveSpace = (Space) findViewById(R.id.space_before_archive_voicemail); + mShareSpace = (Space) findViewById(R.id.space_before_share_voicemail); + mStateText = (TextView) findViewById(R.id.playback_state_text); mPositionText = (TextView) findViewById(R.id.playback_position_text); mTotalDurationText = (TextView) findViewById(R.id.total_duration_text); @@ -241,6 +350,16 @@ public class VoicemailPlaybackLayout extends LinearLayout mStartStopButton.setOnClickListener(mStartStopButtonListener); mPlaybackSpeakerphone.setOnClickListener(mSpeakerphoneListener); mDeleteButton.setOnClickListener(mDeleteButtonListener); + mArchiveButton.setOnClickListener(mArchiveButtonListener); + mShareButton.setOnClickListener(mShareButtonListener); + + mPositionText.setText(formatAsMinutesAndSeconds(0)); + mTotalDurationText.setText(formatAsMinutesAndSeconds(0)); + + mVoicemailSeekHandleEnabled = getResources().getDrawable( + R.drawable.ic_voicemail_seek_handle, mContext.getTheme()); + mVoicemailSeekHandleDisabled = getResources().getDrawable( + R.drawable.ic_voicemail_seek_handle_disabled, mContext.getTheme()); } @Override @@ -249,10 +368,6 @@ public class VoicemailPlaybackLayout extends LinearLayout mStartStopButton.setImageResource(R.drawable.ic_pause); - if (mPresenter != null) { - onSpeakerphoneOn(mPresenter.isSpeakerphoneOn()); - } - if (mPositionUpdater != null) { mPositionUpdater.stopUpdating(); mPositionUpdater = null; @@ -283,12 +398,8 @@ public class VoicemailPlaybackLayout extends LinearLayout mStateText.setText(getString(R.string.voicemail_playback_error)); } - + @Override public void onSpeakerphoneOn(boolean on) { - if (mPresenter != null) { - mPresenter.setSpeakerphoneOn(on); - } - if (on) { mPlaybackSpeakerphone.setImageResource(R.drawable.ic_volume_up_24dp); // Speaker is now on, tapping button will turn it off. @@ -314,13 +425,6 @@ public class VoicemailPlaybackLayout extends LinearLayout mPositionText.setText(formatAsMinutesAndSeconds(seekBarPositionMs)); mTotalDurationText.setText(formatAsMinutesAndSeconds(durationMs)); - mStateText.setText(null); - } - - @Override - public void setIsBuffering() { - disableUiElements(); - mStateText.setText(getString(R.string.voicemail_buffering)); } @Override @@ -331,7 +435,7 @@ public class VoicemailPlaybackLayout extends LinearLayout @Override public void setFetchContentTimeout() { - disableUiElements(); + mStartStopButton.setEnabled(true); mStateText.setText(getString(R.string.voicemail_fetching_timout)); } @@ -343,21 +447,35 @@ public class VoicemailPlaybackLayout extends LinearLayout @Override public void disableUiElements() { mStartStopButton.setEnabled(false); - mPlaybackSpeakerphone.setEnabled(false); - mPlaybackSeek.setProgress(0); - mPlaybackSeek.setEnabled(false); - - mPositionText.setText(formatAsMinutesAndSeconds(0)); - mTotalDurationText.setText(formatAsMinutesAndSeconds(0)); + resetSeekBar(); } @Override public void enableUiElements() { + mDeleteButton.setEnabled(true); mStartStopButton.setEnabled(true); - mPlaybackSpeakerphone.setEnabled(true); mPlaybackSeek.setEnabled(true); + mPlaybackSeek.setThumb(mVoicemailSeekHandleEnabled); } + @Override + public void resetSeekBar() { + mPlaybackSeek.setProgress(0); + mPlaybackSeek.setEnabled(false); + mPlaybackSeek.setThumb(mVoicemailSeekHandleDisabled); + } + + @Override + public void onDeleteCall() {} + + @Override + public void onDeleteVoicemail() { + mPresenter.onVoicemailDeletedInDatabase(); + } + + @Override + public void onGetCallDetails(PhoneCallDetails[] details) {} + private String getString(int resId) { return mContext.getString(resId); } @@ -377,4 +495,139 @@ public class VoicemailPlaybackLayout extends LinearLayout } return String.format("%02d:%02d", minutes, seconds); } + + /** + * Called when a voicemail archive succeeded. If the expanded voicemail was being + * archived, update the card UI. Either way, display a snackbar linking user to archive. + */ + @Override + public void onVoicemailArchiveSucceded(Uri voicemailUri) { + if (isArchiving(voicemailUri)) { + mIsArchiving.remove(voicemailUri); + if (Objects.equals(voicemailUri, mVoicemailUri)) { + onVoicemailArchiveResult(); + hideArchiveButton(); + } + } + + Snackbar.make(this, R.string.snackbar_voicemail_archived, + Snackbar.LENGTH_LONG) + .setDuration(VOICEMAIL_ARCHIVE_DELAY_MS) + .setAction(R.string.snackbar_voicemail_archived_goto, + new View.OnClickListener() { + @Override + public void onClick(View view) { + Intent intent = new Intent(mContext, + VoicemailArchiveActivity.class); + mContext.startActivity(intent); + } + }) + .setActionTextColor( + mContext.getResources().getColor(R.color.dialer_snackbar_action_text_color)) + .show(); + } + + /** + * If a voicemail archive failed, and the expanded card was being archived, update the card UI. + * Either way, display a toast saying the voicemail archive failed. + */ + @Override + public void onVoicemailArchiveFailed(Uri voicemailUri) { + if (isArchiving(voicemailUri)) { + mIsArchiving.remove(voicemailUri); + if (Objects.equals(voicemailUri, mVoicemailUri)) { + onVoicemailArchiveResult(); + } + } + String toastStr = mContext.getString(R.string.voicemail_archive_failed); + Toast.makeText(mContext, toastStr, Toast.LENGTH_SHORT).show(); + } + + public void hideArchiveButton() { + mArchiveSpace.setVisibility(View.GONE); + mArchiveButton.setVisibility(View.GONE); + mArchiveButton.setClickable(false); + mArchiveButton.setEnabled(false); + } + + /** + * Whenever a voicemail archive succeeds or fails, clear the text displayed in the voicemail + * card. + */ + private void onVoicemailArchiveResult() { + enableUiElements(); + mStateText.setText(null); + mArchiveButton.setColorFilter(null); + } + + /** + * Whether or not the voicemail with the given uri is being archived. + */ + private boolean isArchiving(@Nullable Uri uri) { + return uri != null && mIsArchiving.contains(uri); + } + + /** + * Show the proper text and hide the archive button if the voicemail is still being archived. + */ + private void updateArchiveUI(@Nullable Uri voicemailUri) { + if (!Objects.equals(voicemailUri, mVoicemailUri)) { + return; + } + if (isArchiving(voicemailUri)) { + // If expanded card was in the middle of archiving, disable buttons and display message + disableUiElements(); + mDeleteButton.setEnabled(false); + mArchiveButton.setColorFilter(getResources().getColor(R.color.setting_disabled_color)); + mStateText.setText(getString(R.string.voicemail_archiving_content)); + } else { + onVoicemailArchiveResult(); + } + } + + /** + * Hides the archive button if the voicemail has already been archived, shows otherwise. + * @param voicemailUri the URI of the voicemail for which the archive button needs to be updated + */ + private void updateArchiveButton(@Nullable final Uri voicemailUri) { + if (voicemailUri == null || + !Objects.equals(voicemailUri, mVoicemailUri) || isArchiving(voicemailUri) || + Objects.equals(voicemailUri.getAuthority(),VoicemailArchiveContract.AUTHORITY)) { + return; + } + mAsyncTaskExecutor.submit(Tasks.QUERY_ARCHIVED_STATUS, + new AsyncTask<Void, Void, Boolean>() { + @Override + public Boolean doInBackground(Void... params) { + Cursor cursor = mContext.getContentResolver().query(VoicemailArchive.CONTENT_URI, + null, VoicemailArchive.SERVER_ID + "=" + ContentUris.parseId(mVoicemailUri) + + " AND " + VoicemailArchive.ARCHIVED + "= 1", null, null); + boolean archived = cursor != null && cursor.getCount() > 0; + cursor.close(); + return archived; + } + + @Override + public void onPostExecute(Boolean archived) { + if (!Objects.equals(voicemailUri, mVoicemailUri)) { + return; + } + + if (archived) { + hideArchiveButton(); + } else { + mArchiveSpace.setVisibility(View.VISIBLE); + mArchiveButton.setVisibility(View.VISIBLE); + mArchiveButton.setClickable(true); + mArchiveButton.setEnabled(true); + } + + } + }); + } + + @VisibleForTesting + public String getStateText() { + return mStateText.getText().toString(); + } } |