From 267e9ebd056ec4973c4b37c38f949e863de4f24c Mon Sep 17 00:00:00 2001 From: Andrew Lee Date: Tue, 2 Jun 2015 16:43:01 -0700 Subject: Move VM playback from details to call log. - Delete voicemail playback in the CallDetailActivity. + Add voicemail playback to the call log list items. + Move the VoicemailPlaybackPresenter to the CallLogFragment. + Fix some retaining state for both call log (expanded items), and preserve rotation/state functionality for voicemail playback. This included some changes to the Presenter logic. + Fix some tests. Bug: 21471763 Bug: 21170557 Change-Id: I30aae3a52c5bbf74a5075a9666343c337b1fc0df --- res/layout/call_detail.xml | 6 - res/layout/call_log_list_item_actions.xml | 19 +-- res/layout/playback_layout.xml | 92 ------------ res/layout/voicemail_playback_layout.xml | 92 ++++++++++++ src/com/android/dialer/CallDetailActivity.java | 58 -------- src/com/android/dialer/calllog/CallLogAdapter.java | 25 +++- .../android/dialer/calllog/CallLogFragment.java | 17 ++- .../dialer/calllog/CallLogListItemHelper.java | 4 - .../dialer/calllog/CallLogListItemViewHolder.java | 48 +++--- src/com/android/dialer/list/ListsFragment.java | 14 ++ .../dialer/voicemail/VoicemailPlaybackLayout.java | 10 +- .../voicemail/VoicemailPlaybackPresenter.java | 162 +++++++++++---------- src/com/android/dialerbind/ObjectFactory.java | 3 + .../android/dialer/calllog/CallLogAdapterTest.java | 2 +- .../dialer/calllog/CallLogFragmentTest.java | 31 ++-- .../dialer/calllog/CallLogListItemHelperTest.java | 10 +- 16 files changed, 292 insertions(+), 301 deletions(-) delete mode 100644 res/layout/playback_layout.xml create mode 100644 res/layout/voicemail_playback_layout.xml diff --git a/res/layout/call_detail.xml b/res/layout/call_detail.xml index 5d1607edf..c07785159 100644 --- a/res/layout/call_detail.xml +++ b/res/layout/call_detail.xml @@ -87,12 +87,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/res/layout/voicemail_playback_layout.xml b/res/layout/voicemail_playback_layout.xml new file mode 100644 index 000000000..96feba648 --- /dev/null +++ b/res/layout/voicemail_playback_layout.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/com/android/dialer/CallDetailActivity.java b/src/com/android/dialer/CallDetailActivity.java index ce29049d3..6da7c798c 100644 --- a/src/com/android/dialer/CallDetailActivity.java +++ b/src/com/android/dialer/CallDetailActivity.java @@ -61,8 +61,6 @@ import com.android.dialer.calllog.PhoneNumberUtilsWrapper; import com.android.dialer.util.IntentUtil; import com.android.dialer.util.DialerUtils; import com.android.dialer.util.TelecomUtil; -import com.android.dialer.voicemail.VoicemailPlaybackLayout; -import com.android.dialer.voicemail.VoicemailPlaybackPresenter; import java.util.List; @@ -218,8 +216,6 @@ public class CallDetailActivity extends Activity { /** Helper to load contact photos. */ private ContactPhotoManager mContactPhotoManager; - private VoicemailPlaybackPresenter mVoicemailPlaybackPresenter; - private Uri mVoicemailUri; private BidiFormatter mBidiFormatter = BidiFormatter.getInstance(); @@ -255,8 +251,6 @@ public class CallDetailActivity extends Activity { mContactInfoHelper = new ContactInfoHelper(this, GeoUtil.getCurrentCountryIso(this)); getActionBar().setDisplayHomeAsUpEnabled(true); - optionallyHandleVoicemail(); - if (getIntent().getBooleanExtra(EXTRA_FROM_NOTIFICATION, false)) { closeSystemDialogs(); } @@ -269,58 +263,6 @@ public class CallDetailActivity extends Activity { CallLogAsyncTaskUtil.getCallDetails(this, getCallLogEntryUris(), mCallLogAsyncTaskListener); } - @Override - public void onPause() { - if (mVoicemailPlaybackPresenter != null) { - mVoicemailPlaybackPresenter.onPause(isFinishing()); - } - super.onPause(); - } - - @Override - public void onDestroy() { - if (mVoicemailPlaybackPresenter != null) { - mVoicemailPlaybackPresenter.onDestroy(isFinishing()); - } - super.onDestroy(); - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - if (mVoicemailPlaybackPresenter != null) { - mVoicemailPlaybackPresenter.onSaveInstanceState(outState); - } - } - - @Override - public void onRestoreInstanceState(Bundle savedInstanceState) { - if (mVoicemailPlaybackPresenter != null) { - mVoicemailPlaybackPresenter.onRestoreInstanceState(savedInstanceState); - } - super.onRestoreInstanceState(savedInstanceState); - } - - /** - * Handle voicemail playback or hide voicemail ui. - *

- * If the Intent used to start this Activity contains the suitable extras, then start voicemail - * playback. If it doesn't, then don't inflate the voicemail ui. - */ - private void optionallyHandleVoicemail() { - if (hasVoicemail()) { - VoicemailPlaybackLayout voicemailPlaybackLayout = - (VoicemailPlaybackLayout) findViewById(R.id.voicemail_playback_layout); - - mVoicemailPlaybackPresenter = new VoicemailPlaybackPresenter(this); - mVoicemailPlaybackPresenter.setPlaybackView( - voicemailPlaybackLayout, mVoicemailUri, false /* startPlayingImmediately */); - - voicemailPlaybackLayout.setVisibility(View.VISIBLE); - CallLogAsyncTaskUtil.markVoicemailAsRead(this, mVoicemailUri); - } - } - private boolean hasVoicemail() { return mVoicemailUri != null; } diff --git a/src/com/android/dialer/calllog/CallLogAdapter.java b/src/com/android/dialer/calllog/CallLogAdapter.java index c472ef2ca..6862f68e2 100644 --- a/src/com/android/dialer/calllog/CallLogAdapter.java +++ b/src/com/android/dialer/calllog/CallLogAdapter.java @@ -22,6 +22,7 @@ import android.content.res.Resources; import android.database.Cursor; import android.net.Uri; import android.support.v7.widget.RecyclerView; +import android.os.Bundle; import android.os.Trace; import android.support.v7.widget.RecyclerView.ViewHolder; import android.telecom.PhoneAccountHandle; @@ -41,6 +42,7 @@ import com.android.dialer.R; import com.android.dialer.contactinfo.ContactInfoCache; import com.android.dialer.contactinfo.ContactInfoCache.OnContactInfoChangedListener; import com.android.dialer.util.DialerUtils; +import com.android.dialer.voicemail.VoicemailPlaybackPresenter; import com.google.common.annotations.VisibleForTesting; @@ -67,6 +69,7 @@ public class CallLogAdapter extends GroupingListAdapter protected final Context mContext; private final ContactInfoHelper mContactInfoHelper; + private final VoicemailPlaybackPresenter mVoicemailPlaybackPresenter; private final CallFetcher mCallFetcher; private final OnReportButtonClickListener mOnReportButtonClickListener; private ViewTreeObserver mViewTreeObserver = null; @@ -75,6 +78,9 @@ public class CallLogAdapter extends GroupingListAdapter private boolean mIsShowingRecentsTab; + private static final String KEY_EXPANDED_POSITION = "expanded_position"; + private static final String KEY_EXPANDED_ROW_ID = "expanded_row_id"; + // Tracks the position of the currently expanded list item. private int mCurrentlyExpandedPosition = RecyclerView.NO_POSITION; // Tracks the rowId of the currently expanded list item, so the position can be updated if there @@ -200,6 +206,7 @@ public class CallLogAdapter extends GroupingListAdapter Context context, CallFetcher callFetcher, ContactInfoHelper contactInfoHelper, + VoicemailPlaybackPresenter voicemailPlaybackPresenter, boolean isShowingRecentsTab, OnReportButtonClickListener onReportButtonClickListener) { super(context); @@ -207,6 +214,7 @@ public class CallLogAdapter extends GroupingListAdapter mContext = context; mCallFetcher = callFetcher; mContactInfoHelper = contactInfoHelper; + mVoicemailPlaybackPresenter = voicemailPlaybackPresenter; mIsShowingRecentsTab = isShowingRecentsTab; mOnReportButtonClickListener = onReportButtonClickListener; @@ -226,6 +234,20 @@ public class CallLogAdapter extends GroupingListAdapter mCallLogGroupBuilder = new CallLogGroupBuilder(this); } + public void onSaveInstanceState(Bundle outState) { + outState.putInt(KEY_EXPANDED_POSITION, mCurrentlyExpandedPosition); + outState.putLong(KEY_EXPANDED_ROW_ID, mCurrentlyExpandedRowId); + } + + public void onRestoreInstanceState(Bundle savedInstanceState) { + if (savedInstanceState != null) { + mCurrentlyExpandedPosition = + savedInstanceState.getInt(KEY_EXPANDED_POSITION, RecyclerView.NO_POSITION); + mCurrentlyExpandedRowId = + savedInstanceState.getLong(KEY_EXPANDED_ROW_ID, NO_EXPANDED_LIST_ITEM); + } + } + /** * Requery on background thread when {@link Cursor} changes. */ @@ -296,7 +318,8 @@ public class CallLogAdapter extends GroupingListAdapter mContext, mActionListener, mPhoneNumberUtilsWrapper, - mCallLogViewsHelper); + mCallLogViewsHelper, + mVoicemailPlaybackPresenter); viewHolder.callLogEntryView.setTag(viewHolder); viewHolder.callLogEntryView.setAccessibilityDelegate(mAccessibilityDelegate); diff --git a/src/com/android/dialer/calllog/CallLogFragment.java b/src/com/android/dialer/calllog/CallLogFragment.java index 36d9bb6ea..845f91108 100644 --- a/src/com/android/dialer/calllog/CallLogFragment.java +++ b/src/com/android/dialer/calllog/CallLogFragment.java @@ -51,6 +51,7 @@ import com.android.dialer.R; import com.android.dialer.list.ListsFragment.HostInterface; import com.android.dialer.util.DialerUtils; import com.android.dialer.util.EmptyLoader; +import com.android.dialer.voicemail.VoicemailPlaybackPresenter; import com.android.dialer.voicemail.VoicemailStatusHelper; import com.android.dialer.voicemail.VoicemailStatusHelper.StatusMessage; import com.android.dialer.voicemail.VoicemailStatusHelperImpl; @@ -87,6 +88,7 @@ public class CallLogFragment extends Fragment private LinearLayoutManager mLayoutManager; private CallLogAdapter mAdapter; private CallLogQueryHandler mCallLogQueryHandler; + private VoicemailPlaybackPresenter mVoicemailPlaybackPresenter; private boolean mScrollToTop; /** Whether there is at least one voicemail source installed. */ @@ -187,6 +189,8 @@ public class CallLogFragment extends Fragment mContactsObserver); resolver.registerContentObserver(Status.CONTENT_URI, true, mVoicemailStatusObserver); setHasOptionsMenu(true); + + mVoicemailPlaybackPresenter = new VoicemailPlaybackPresenter(activity, state); } /** Called by the CallLogQueryHandler when the list of calls has been fetched or updated. */ @@ -272,6 +276,7 @@ public class CallLogFragment extends Fragment getActivity(), this, new ContactInfoHelper(getActivity(), currentCountryIso), + mVoicemailPlaybackPresenter, isShowingRecentsTab, this); mRecyclerView.setAdapter(mAdapter); @@ -283,7 +288,9 @@ public class CallLogFragment extends Fragment @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); + updateEmptyMessage(mCallTypeFilter); + mAdapter.onRestoreInstanceState(savedInstanceState); } /** @@ -318,8 +325,9 @@ public class CallLogFragment extends Fragment @Override public void onPause() { - super.onPause(); + mVoicemailPlaybackPresenter.onPause(getActivity().isFinishing()); mAdapter.pauseCache(); + super.onPause(); } @Override @@ -331,12 +339,14 @@ public class CallLogFragment extends Fragment @Override public void onDestroy() { - super.onDestroy(); mAdapter.pauseCache(); mAdapter.changeCursor(null); + mVoicemailPlaybackPresenter.onDestroy(getActivity().isFinishing()); + getActivity().getContentResolver().unregisterContentObserver(mCallLogObserver); getActivity().getContentResolver().unregisterContentObserver(mContactsObserver); getActivity().getContentResolver().unregisterContentObserver(mVoicemailStatusObserver); + super.onDestroy(); } @Override @@ -345,6 +355,9 @@ public class CallLogFragment extends Fragment outState.putInt(KEY_FILTER_TYPE, mCallTypeFilter); outState.putInt(KEY_LOG_LIMIT, mLogLimit); outState.putLong(KEY_DATE_LIMIT, mDateLimit); + + mAdapter.onSaveInstanceState(outState); + mVoicemailPlaybackPresenter.onSaveInstanceState(outState); } @Override diff --git a/src/com/android/dialer/calllog/CallLogListItemHelper.java b/src/com/android/dialer/calllog/CallLogListItemHelper.java index 147a192e3..4eb8797ee 100644 --- a/src/com/android/dialer/calllog/CallLogListItemHelper.java +++ b/src/com/android/dialer/calllog/CallLogListItemHelper.java @@ -91,10 +91,6 @@ import com.android.dialer.R; mResources.getString(R.string.description_video_call_action), nameOrNumber)); - views.voicemailButtonView.setContentDescription( - TextUtils.expandTemplate( - mResources.getString(R.string.description_voicemail_action), nameOrNumber)); - views.createNewContactButtonView.setContentDescription( TextUtils.expandTemplate( mResources.getString(R.string.description_create_new_contact_action), diff --git a/src/com/android/dialer/calllog/CallLogListItemViewHolder.java b/src/com/android/dialer/calllog/CallLogListItemViewHolder.java index ccd480ec6..5125247aa 100644 --- a/src/com/android/dialer/calllog/CallLogListItemViewHolder.java +++ b/src/com/android/dialer/calllog/CallLogListItemViewHolder.java @@ -41,6 +41,9 @@ import com.android.contacts.common.util.UriUtils; import com.android.dialer.PhoneCallDetailsHelper; import com.android.dialer.PhoneCallDetailsViews; import com.android.dialer.R; +import com.android.dialer.calllog.CallLogAsyncTaskUtil; +import com.android.dialer.voicemail.VoicemailPlaybackPresenter; +import com.android.dialer.voicemail.VoicemailPlaybackLayout; /** * This is an object containing references to views contained by the call log list item. This @@ -68,8 +71,8 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder { /** The view containing call log item actions. Null until the ViewStub is inflated. */ public View actionsView; /** The button views below are assigned only when the action section is expanded. */ + public VoicemailPlaybackLayout voicemailPlaybackView; public View videoCallButtonView; - public View voicemailButtonView; public View createNewContactButtonView; public View addToExistingContactButtonView; public View sendMessageView; @@ -142,6 +145,7 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder { private final View.OnClickListener mActionListener; private final PhoneNumberUtilsWrapper mPhoneNumberUtilsWrapper; private final CallLogListItemHelper mCallLogListItemHelper; + private final VoicemailPlaybackPresenter mVoicemailPlaybackPresenter; private final int mPhotoSize; @@ -150,6 +154,7 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder { View.OnClickListener actionListener, PhoneNumberUtilsWrapper phoneNumberUtilsWrapper, CallLogListItemHelper callLogListItemHelper, + VoicemailPlaybackPresenter voicemailPlaybackPresenter, View rootView, QuickContactBadge quickContactView, View primaryActionView, @@ -163,6 +168,7 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder { mActionListener = actionListener; mPhoneNumberUtilsWrapper = phoneNumberUtilsWrapper; mCallLogListItemHelper = callLogListItemHelper; + mVoicemailPlaybackPresenter = voicemailPlaybackPresenter; this.rootView = rootView; this.quickContactView = quickContactView; @@ -191,13 +197,15 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder { Context context, View.OnClickListener actionListener, PhoneNumberUtilsWrapper phoneNumberUtilsWrapper, - CallLogListItemHelper callLogListItemHelper) { + CallLogListItemHelper callLogListItemHelper, + VoicemailPlaybackPresenter voicemailPlaybackPresenter) { return new CallLogListItemViewHolder( context, actionListener, phoneNumberUtilsWrapper, callLogListItemHelper, + voicemailPlaybackPresenter, view, (QuickContactBadge) view.findViewById(R.id.quick_contact_photo), view.findViewById(R.id.primary_action_view), @@ -220,12 +228,12 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder { if (stub != null) { actionsView = (ViewGroup) stub.inflate(); + voicemailPlaybackView = (VoicemailPlaybackLayout) actionsView + .findViewById(R.id.voicemail_playback_layout); + videoCallButtonView = actionsView.findViewById(R.id.video_call_action); videoCallButtonView.setOnClickListener(mActionListener); - voicemailButtonView = actionsView.findViewById(R.id.voicemail_action); - voicemailButtonView.setOnClickListener(mActionListener); - createNewContactButtonView = actionsView.findViewById(R.id.create_new_contact_action); createNewContactButtonView.setOnClickListener(mActionListener); @@ -302,29 +310,30 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder { videoCallButtonView.setVisibility(View.GONE); } - // For voicemail calls, show the "VOICEMAIL" action button; hide otherwise. - if (callType == Calls.VOICEMAIL_TYPE) { - voicemailButtonView.setTag( - IntentProvider.getPlayVoicemailIntentProvider(rowId, voicemailUri)); - voicemailButtonView.setVisibility(View.VISIBLE); + // For voicemail calls, show the voicemail playback layout; hide otherwise. + if (callType == Calls.VOICEMAIL_TYPE && mVoicemailPlaybackPresenter != null) { + voicemailPlaybackView.setVisibility(View.VISIBLE); - detailsButtonView.setVisibility(View.GONE); - } else { - voicemailButtonView.setTag(null); - voicemailButtonView.setVisibility(View.GONE); + Uri uri = Uri.parse(voicemailUri); + mVoicemailPlaybackPresenter.setPlaybackView( + voicemailPlaybackView, uri, false /* startPlayingImmediately */); - detailsButtonView.setVisibility(View.VISIBLE); - detailsButtonView.setTag( - IntentProvider.getCallDetailIntentProvider(rowId, callIds, null)); + CallLogAsyncTaskUtil.markVoicemailAsRead(mContext, uri); + } else { + voicemailPlaybackView.setVisibility(View.GONE); } + detailsButtonView.setVisibility(View.VISIBLE); + detailsButtonView.setTag( + IntentProvider.getCallDetailIntentProvider(rowId, callIds, null)); + if (canBeReportedAsInvalid && !info.isBadData) { reportButtonView.setVisibility(View.VISIBLE); } else { reportButtonView.setVisibility(View.GONE); } - if (UriUtils.isEncodedContactUri(info.lookupUri)) { + if (info != null && UriUtils.isEncodedContactUri(info.lookupUri)) { createNewContactButtonView.setTag(IntentProvider.getAddContactIntentProvider( info.lookupUri, info.name, info.number, info.type, true /* isNewContact */)); createNewContactButtonView.setVisibility(View.VISIBLE); @@ -420,6 +429,7 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder { null /* actionListener */, phoneNumberUtilsWrapper, new CallLogListItemHelper(phoneCallDetailsHelper, resources), + null /* voicemailPlaybackPresenter */, new View(context), new QuickContactBadge(context), new View(context), @@ -427,10 +437,10 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder { new CardView(context), new TextView(context), new View(context)); - viewHolder.voicemailButtonView = new TextView(context); viewHolder.detailsButtonView = new TextView(context); viewHolder.reportButtonView = new TextView(context); viewHolder.actionsView = new View(context); + viewHolder.voicemailPlaybackView = new VoicemailPlaybackLayout(context); return viewHolder; } diff --git a/src/com/android/dialer/list/ListsFragment.java b/src/com/android/dialer/list/ListsFragment.java index a0e443c22..49226d61e 100644 --- a/src/com/android/dialer/list/ListsFragment.java +++ b/src/com/android/dialer/list/ListsFragment.java @@ -62,6 +62,9 @@ public class ListsFragment extends Fragment // Oldest recents entry to display is 2 weeks old. private static final long OLDEST_RECENTS_DATE = 1000L * 60 * 60 * 24 * 14; + private static final String KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER = + "has_active_voicemail_provider"; + public interface HostInterface { public ActionBarController getActionBarController(); } @@ -161,6 +164,11 @@ public class ListsFragment extends Fragment Trace.endSection(); mVoicemailStatusHelper = new VoicemailStatusHelperImpl(); + + if (savedInstanceState != null) { + mHasActiveVoicemailProvider = savedInstanceState.getBoolean( + KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER, false); + } Trace.endSection(); } @@ -226,6 +234,12 @@ public class ListsFragment extends Fragment } } + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putBoolean(KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER, mHasActiveVoicemailProvider); + } + @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { mTabIndex = getRtlPosition(position); diff --git a/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java b/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java index 0e9ff3bdc..703004d07 100644 --- a/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java +++ b/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java @@ -76,10 +76,9 @@ public class VoicemailPlaybackLayout extends LinearLayout public PositionUpdater( MediaPlayer mediaPlayer, - int duration, ScheduledExecutorService executorService) { mMediaPlayer = mediaPlayer; - mDuration = duration; + mDuration = mediaPlayer.getDuration(); mExecutorService = executorService; } @@ -167,6 +166,7 @@ public class VoicemailPlaybackLayout extends LinearLayout if (mPresenter == null) { return; } + if (mIsPlaying) { mPresenter.pausePlayback(); } else { @@ -197,7 +197,7 @@ public class VoicemailPlaybackLayout extends LinearLayout mContext = context; LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - inflater.inflate(R.layout.playback_layout, this); + inflater.inflate(R.layout.voicemail_playback_layout, this); } @Override @@ -222,7 +222,6 @@ public class VoicemailPlaybackLayout extends LinearLayout @Override public void onPlaybackStarted( MediaPlayer mediaPlayer, - int duration, ScheduledExecutorService executorService) { mIsPlaying = true; @@ -232,7 +231,7 @@ public class VoicemailPlaybackLayout extends LinearLayout onSpeakerphoneOn(mPresenter.isSpeakerphoneOn()); } - mPositionUpdater = new PositionUpdater(mediaPlayer, duration, executorService); + mPositionUpdater = new PositionUpdater(mediaPlayer, executorService); mPositionUpdater.startUpdating(); } @@ -286,6 +285,7 @@ public class VoicemailPlaybackLayout extends LinearLayout if (mPlaybackSeek.getMax() != seekBarMax) { mPlaybackSeek.setMax(seekBarMax); } + mPlaybackSeek.setProgress(seekBarPosition); mPlaybackPosition.setText(formatAsMinutesAndSeconds(seekBarMax - seekBarPosition)); } diff --git a/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java b/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java index e8b04602d..99d2734eb 100644 --- a/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java +++ b/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java @@ -78,9 +78,9 @@ public class VoicemailPlaybackPresenter void disableUiElements(); void enableUiElements(); void onPlaybackError(Exception e); - void onPlaybackStarted(MediaPlayer mediaPlayer, int duration, - ScheduledExecutorService executorService); + void onPlaybackStarted(MediaPlayer mediaPlayer, ScheduledExecutorService executorService); void onPlaybackStopped(); + void onSpeakerphoneOn(boolean on); void setClipPosition(int clipPositionInMillis, int clipLengthInMillis); void setFetchContentTimeout(); void setIsBuffering(); @@ -103,6 +103,10 @@ public class VoicemailPlaybackPresenter // Time to wait for content to be fetched before timing out. private static final long FETCH_CONTENT_TIMEOUT_MS = 20000; + private static final String VOICEMAIL_URI_KEY = + VoicemailPlaybackPresenter.class.getName() + ".VOICEMAIL_URI"; + private static final String IS_PREPARED_KEY = + VoicemailPlaybackPresenter.class.getName() + ".IS_PREPARED"; // If present in the saved instance bundle, we should not resume playback on create. private static final String IS_PLAYING_STATE_KEY = VoicemailPlaybackPresenter.class.getName() + ".IS_PLAYING_STATE_KEY"; @@ -111,22 +115,21 @@ public class VoicemailPlaybackPresenter VoicemailPlaybackPresenter.class.getName() + ".CLIP_POSITION_KEY"; /** - * The most recently calculated duration. - *

- * We cache this in a field since we don't want to keep requesting it from the player, as - * this can easily lead to throwing {@link IllegalStateException} (any time the player is - * released, it's illegal to ask for the duration). + * The most recently cached duration. We cache this since we don't want to keep requesting it + * from the player, as this can easily lead to throwing {@link IllegalStateException} (any time + * the player is released, it's illegal to ask for the duration). */ private final AtomicInteger mDuration = new AtomicInteger(0); private Context mContext; - private MediaPlayer mMediaPlayer; private PlaybackView mView; + private static MediaPlayer mMediaPlayer; private Uri mVoicemailUri; private int mPosition; private boolean mIsPrepared; private boolean mIsPlaying; + private boolean mShouldResumePlaybackAfterSeeking; // Used to run async tasks that need to interact with the UI. @@ -141,11 +144,19 @@ public class VoicemailPlaybackPresenter private PowerManager.WakeLock mProximityWakeLock; private AudioManager mAudioManager; - public VoicemailPlaybackPresenter(Activity activity) { + public VoicemailPlaybackPresenter(Activity activity, Bundle savedInstanceState) { mContext = activity; mAsyncTaskExecutor = AsyncTaskExecutors.createAsyncTaskExecutor(); mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + if (savedInstanceState != null) { + // Restores playback state when activity is recreated, such as after rotation. + mVoicemailUri = (Uri) savedInstanceState.getParcelable(VOICEMAIL_URI_KEY); + mIsPrepared = savedInstanceState.getBoolean(IS_PREPARED_KEY); + mPosition = savedInstanceState.getInt(CLIP_POSITION_KEY, 0); + mIsPlaying = savedInstanceState.getBoolean(IS_PLAYING_STATE_KEY, false); + } + PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); if (powerManager.isWakeLockLevelSupported(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)) { @@ -153,12 +164,15 @@ public class VoicemailPlaybackPresenter PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, TAG); } + // mMediaPlayer is static to enable seamless playback during rotation. If we do not create + // a new MediaPlayer, we still need to update listeners to the current Presenter instance. if (mMediaPlayer == null) { mMediaPlayer = new MediaPlayer(); - mMediaPlayer.setOnPreparedListener(this); - mMediaPlayer.setOnErrorListener(this); - mMediaPlayer.setOnCompletionListener(this); + mIsPrepared = false; } + mMediaPlayer.setOnPreparedListener(this); + mMediaPlayer.setOnErrorListener(this); + mMediaPlayer.setOnCompletionListener(this); activity.setVolumeControlStream(PLAYBACK_STREAM); } @@ -169,21 +183,29 @@ public class VoicemailPlaybackPresenter public void setPlaybackView( PlaybackView view, Uri voicemailUri, boolean startPlayingImmediately) { mView = view; - mVoicemailUri = voicemailUri; - mView.setPresenter(this); - if (!mMediaPlayer.isPlaying()) { - setPosition(0, startPlayingImmediately); - mIsPrepared = false; + mView.onSpeakerphoneOn(isSpeakerphoneOn()); + + if (mVoicemailUri != null && mVoicemailUri.equals(voicemailUri)) { + // Handles rotation case where playback view is set for the same voicemail. + if (mIsPrepared) { + onPrepared(mMediaPlayer); + } else { + checkForContent(); + } + } else { + mVoicemailUri = voicemailUri; + mPosition = 0; + mIsPlaying = startPlayingImmediately; + checkForContent(); } - checkForContent(); } public void onPause(boolean isFinishing) { // Do not pause for orientation changes. if (mMediaPlayer.isPlaying() && isFinishing) { - pausePlayback(mMediaPlayer.getCurrentPosition(), mIsPlaying); + pausePlayback(); } disableProximitySensor(false /* waitForFarState */); @@ -210,16 +232,11 @@ public class VoicemailPlaybackPresenter } public void onSaveInstanceState(Bundle outState) { - outState.putInt(CLIP_POSITION_KEY, mView.getDesiredClipPosition()); - outState.putBoolean(IS_PLAYING_STATE_KEY, mIsPlaying); - } - - public void onRestoreInstanceState(Bundle inState) { - if (inState != null) { - int position = inState.getInt(CLIP_POSITION_KEY, 0); - boolean isPlaying = inState.getBoolean(IS_PLAYING_STATE_KEY, false); - // Playback will be automatically resumed, if appropriate, in onPrepared(). - setPosition(position, isPlaying); + if (mView != null) { + outState.putParcelable(VOICEMAIL_URI_KEY, mVoicemailUri); + outState.putBoolean(IS_PREPARED_KEY, mIsPrepared); + outState.putInt(CLIP_POSITION_KEY, mView.getDesiredClipPosition()); + outState.putBoolean(IS_PLAYING_STATE_KEY, mIsPlaying); } } @@ -355,6 +372,7 @@ public class VoicemailPlaybackPresenter * and it will call {@link #onError()} otherwise. */ private void prepareToPlayContent() { + mIsPrepared = false; mView.setIsBuffering(); try { @@ -373,8 +391,10 @@ public class VoicemailPlaybackPresenter @Override public void onPrepared(MediaPlayer mp) { mIsPrepared = true; + mDuration.set(mMediaPlayer.getDuration()); mView.enableUiElements(); + mView.setClipPosition(mPosition, mDuration.get()); if (mIsPlaying) { resumePlayback(); @@ -400,7 +420,9 @@ public class VoicemailPlaybackPresenter } mView.onPlaybackError(e); - setPosition(0, false); + + mPosition = 0; + mIsPlaying = false; } /** @@ -408,7 +430,11 @@ public class VoicemailPlaybackPresenter */ @Override public void onCompletion(MediaPlayer mediaPlayer) { - pausePlayback(0, false); + pausePlayback(); + + // Reset the seekbar position to the beginning. + mPosition = 0; + mView.setClipPosition(0, mDuration.get()); } @Override @@ -423,56 +449,45 @@ public class VoicemailPlaybackPresenter } /** - * Sets the position and playing state for when playback is resumed. - */ - private void setPosition(int position, boolean isPlaying) { - mPosition = position; - mIsPlaying = isPlaying; - } - - /** - * Resumes voicemail playback at the clip position stored by the presenter. + * Resumes voicemail playback at the clip position stored by the presenter. Null-op if already + * playing. */ public void resumePlayback() { - final int duration = mMediaPlayer.getDuration(); - mDuration.set(duration); + mIsPlaying = true; - // Clamp the start position between 0 and the duration. - int startPosition = Math.max(0, Math.min(mPosition, duration)); - mMediaPlayer.seekTo(startPosition); - setPosition(startPosition, true); + if (!mMediaPlayer.isPlaying()) { + // Clamp the start position between 0 and the duration. + mPosition = Math.max(0, Math.min(mPosition, mDuration.get())); + mMediaPlayer.seekTo(mPosition); + + try { + // Grab audio focus here + int result = mAudioManager.requestAudioFocus( + VoicemailPlaybackPresenter.this, + PLAYBACK_STREAM, + AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); + + if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { + throw new RejectedExecutionException("Could not capture audio focus."); + } - try { - // Grab audio focus here - int result = mAudioManager.requestAudioFocus( - VoicemailPlaybackPresenter.this, - PLAYBACK_STREAM, - AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); - - if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { - throw new RejectedExecutionException("Could not capture audio focus."); + // Can throw RejectedExecutionException + mMediaPlayer.start(); + } catch (RejectedExecutionException e) { + handleError(e); } - - // Can throw RejectedExecutionException - mMediaPlayer.start(); - - mView.onPlaybackStarted(mMediaPlayer, duration, getScheduledExecutorServiceInstance()); - enableProximitySensor(); - } catch (RejectedExecutionException e) { - handleError(e); } - } - public void pausePlayback() { - pausePlayback(mMediaPlayer.getCurrentPosition(), false); + enableProximitySensor(); + mView.onPlaybackStarted(mMediaPlayer, getScheduledExecutorServiceInstance()); } /** - * {@link isPlaying} may be set to {@code true} so voicemail playback can be resumed after a - * rotation. + * Pauses voicemail playback at the current position. Null-op if already paused. */ - private void pausePlayback(int position, boolean isPlaying) { - setPosition(position, isPlaying); + public void pausePlayback() { + mPosition = mMediaPlayer.getCurrentPosition(); + mIsPlaying = false; if (mMediaPlayer.isPlaying()) { mMediaPlayer.pause(); @@ -483,9 +498,6 @@ public class VoicemailPlaybackPresenter // Always disable the proximity sensor on stop. disableProximitySensor(true /* waitForFarState */); - - int duration = mDuration.get(); - mView.setClipPosition(position, duration); } /** @@ -498,11 +510,11 @@ public class VoicemailPlaybackPresenter } public void resumePlaybackAfterSeeking(int desiredPosition) { - setPosition(desiredPosition, mShouldResumePlaybackAfterSeeking); + mPosition = desiredPosition; if (mShouldResumePlaybackAfterSeeking) { + mShouldResumePlaybackAfterSeeking = false; resumePlayback(); } - mShouldResumePlaybackAfterSeeking = false; } private void enableProximitySensor() { diff --git a/src/com/android/dialerbind/ObjectFactory.java b/src/com/android/dialerbind/ObjectFactory.java index 12607d9f3..43b237c1b 100644 --- a/src/com/android/dialerbind/ObjectFactory.java +++ b/src/com/android/dialerbind/ObjectFactory.java @@ -25,6 +25,7 @@ import com.android.dialer.calllog.CallLogAdapter; import com.android.dialer.calllog.CallLogAdapter.OnReportButtonClickListener; import com.android.dialer.calllog.ContactInfoHelper; import com.android.dialer.service.CachedNumberLookupService; +import com.android.dialer.voicemail.VoicemailPlaybackPresenter; /** * Default static binding for various objects. @@ -47,12 +48,14 @@ public class ObjectFactory { Context context, CallFetcher callFetcher, ContactInfoHelper contactInfoHelper, + VoicemailPlaybackPresenter voicemailPlaybackPresenter, boolean isShowingRecentsTab, OnReportButtonClickListener onReportButtonClickListener) { return new CallLogAdapter( context, callFetcher, contactInfoHelper, + voicemailPlaybackPresenter, isShowingRecentsTab, onReportButtonClickListener); } diff --git a/tests/src/com/android/dialer/calllog/CallLogAdapterTest.java b/tests/src/com/android/dialer/calllog/CallLogAdapterTest.java index 5f09cb74b..14e0aaf9e 100644 --- a/tests/src/com/android/dialer/calllog/CallLogAdapterTest.java +++ b/tests/src/com/android/dialer/calllog/CallLogAdapterTest.java @@ -204,7 +204,7 @@ public class CallLogAdapterTest extends AndroidTestCase { private static final class TestCallLogAdapter extends CallLogAdapter { public TestCallLogAdapter(Context context, CallFetcher callFetcher, ContactInfoHelper contactInfoHelper) { - super(context, callFetcher, contactInfoHelper, false, null); + super(context, callFetcher, contactInfoHelper, null, false, null); mContactInfoCache = new TestContactInfoCache( contactInfoHelper, mOnContactInfoChangedListener); } diff --git a/tests/src/com/android/dialer/calllog/CallLogFragmentTest.java b/tests/src/com/android/dialer/calllog/CallLogFragmentTest.java index 0c19799cc..71554d6e1 100644 --- a/tests/src/com/android/dialer/calllog/CallLogFragmentTest.java +++ b/tests/src/com/android/dialer/calllog/CallLogFragmentTest.java @@ -335,28 +335,15 @@ public class CallLogFragmentTest extends ActivityInstrumentationTestCase2