Create the voicemail fragment on-demand, not every time.

Main changes:
- Remove fragment from call details xml, leave a placeholder comment.
- Add fragment dynamically using fragment manager if desired.
- Change api for setting voicemail and whether to start playing:
  accept the uri and start playing as constructor parameters for
  controller, have the fragment parse them from the arguments bundle.

Other changes:
- Inflation of fragment requires ignoring the parent.
- Changed locking strategy when updating clip position, it's only done
  now when getting the current position.  This guarantees that we don't
  try to get position after scheduled runnable is null (which should
  prevent us trying to call get position once we've released the player,
  which was the source of a crash I would rarely see when using the back
  button).

Bug: 5048103
Change-Id: Ie82708184295500473b5d2c5db010cdd332acb8a
diff --git a/res/layout/call_detail.xml b/res/layout/call_detail.xml
index 02dd098..6e1f4fd 100644
--- a/res/layout/call_detail.xml
+++ b/res/layout/call_detail.xml
@@ -47,12 +47,7 @@
         android:layout_height="wrap_content"
         android:layout_below="@id/contact_background"
     >
-        <fragment
-            class="com.android.contacts.voicemail.VoicemailPlaybackFragment"
-            android:id="@+id/voicemail_playback_fragment"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-        />
+        <!-- The voicemail fragment will be put here. -->
     </LinearLayout>
     <LinearLayout
         android:layout_width="match_parent"
diff --git a/src/com/android/contacts/CallDetailActivity.java b/src/com/android/contacts/CallDetailActivity.java
index 813ebb1..e9a693e 100644
--- a/src/com/android/contacts/CallDetailActivity.java
+++ b/src/com/android/contacts/CallDetailActivity.java
@@ -25,7 +25,6 @@
 import com.android.contacts.voicemail.VoicemailStatusHelperImpl;
 
 import android.app.ActionBar;
-import android.app.FragmentManager;
 import android.app.ListActivity;
 import android.content.ContentResolver;
 import android.content.ContentUris;
@@ -179,16 +178,22 @@
      */
     private void optionallyHandleVoicemail() {
         if (hasVoicemail()) {
-            // Has voicemail: leave the fragment visible.  Optionally start the playback.
+            // Has voicemail: add the voicemail fragment.  Add suitable arguments to set the uri
+            // to play and optionally start the playback.
             // Do a query to fetch the voicemail status messages.
-            boolean startPlayback = getIntent().getBooleanExtra(
-                    EXTRA_VOICEMAIL_START_PLAYBACK, false);
+            VoicemailPlaybackFragment playbackFragment = new VoicemailPlaybackFragment();
+            Bundle fragmentArguments = new Bundle();
             Uri voicemailUri = getIntent().getParcelableExtra(EXTRA_VOICEMAIL_URI);
-            getVoicemailPlaybackFragment().setVoicemailUri(voicemailUri, startPlayback);
+            fragmentArguments.putParcelable(EXTRA_VOICEMAIL_URI, voicemailUri);
+            if (getIntent().getBooleanExtra(EXTRA_VOICEMAIL_START_PLAYBACK, false)) {
+                fragmentArguments.putBoolean(EXTRA_VOICEMAIL_START_PLAYBACK, true);
+            }
+            playbackFragment.setArguments(fragmentArguments);
+            getFragmentManager().beginTransaction()
+                    .add(R.id.voicemail_container, playbackFragment).commit();
             mAsyncQueryHandler.startVoicemailStatusQuery(voicemailUri);
         } else {
-            // No voicemail uri: hide the voicemail fragment and the status view.
-            getFragmentManager().beginTransaction().hide(getVoicemailPlaybackFragment()).commit();
+            // No voicemail uri: hide the status view.
             mStatusMessageView.setVisibility(View.GONE);
         }
     }
@@ -197,12 +202,6 @@
         return getIntent().getParcelableExtra(EXTRA_VOICEMAIL_URI) != null;
     }
 
-    private VoicemailPlaybackFragment getVoicemailPlaybackFragment() {
-        FragmentManager manager = getFragmentManager();
-        return (VoicemailPlaybackFragment) manager.findFragmentById(
-                R.id.voicemail_playback_fragment);
-    }
-
     /**
      * Returns the list of URIs to show.
      * <p>
diff --git a/src/com/android/contacts/voicemail/VoicemailPlaybackFragment.java b/src/com/android/contacts/voicemail/VoicemailPlaybackFragment.java
index b1da1a1..edc1bb4 100644
--- a/src/com/android/contacts/voicemail/VoicemailPlaybackFragment.java
+++ b/src/com/android/contacts/voicemail/VoicemailPlaybackFragment.java
@@ -16,9 +16,13 @@
 
 package com.android.contacts.voicemail;
 
+import static com.android.contacts.CallDetailActivity.EXTRA_VOICEMAIL_START_PLAYBACK;
+import static com.android.contacts.CallDetailActivity.EXTRA_VOICEMAIL_URI;
+
 import com.android.contacts.R;
 import com.android.ex.variablespeed.MediaPlayerProxy;
 import com.android.ex.variablespeed.VariableSpeed;
+import com.google.common.base.Preconditions;
 
 import android.app.Fragment;
 import android.content.Context;
@@ -71,7 +75,7 @@
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
-        View view = inflater.inflate(R.layout.playback_layout, container);
+        View view = inflater.inflate(R.layout.playback_layout, null);
         mPlaybackSeek = (SeekBar) view.findViewById(R.id.playback_seek);
         mPlaybackSeek = (SeekBar) view.findViewById(R.id.playback_seek);
         mStartStopButton = (ImageButton) view.findViewById(R.id.playback_start_stop);
@@ -88,8 +92,14 @@
     public void onActivityCreated(Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
         mScheduledExecutorService = createScheduledExecutorService();
+        Bundle arguments = getArguments();
+        Preconditions.checkNotNull(arguments, "fragment must be started with arguments");
+        Uri voicemailUri = arguments.getParcelable(EXTRA_VOICEMAIL_URI);
+        Preconditions.checkNotNull(voicemailUri, "fragment must contain EXTRA_VOICEMAIL_URI");
+        boolean startPlayback = arguments.getBoolean(EXTRA_VOICEMAIL_START_PLAYBACK, false);
         mPresenter = new VoicemailPlaybackPresenter(new PlaybackViewImpl(),
-                createMediaPlayer(mScheduledExecutorService), mScheduledExecutorService);
+                createMediaPlayer(mScheduledExecutorService), voicemailUri,
+                mScheduledExecutorService, startPlayback);
         mPresenter.onCreate(savedInstanceState);
     }
 
@@ -106,11 +116,6 @@
         super.onDestroy();
     }
 
-    /** Call this from the Activity containing this fragment to set the voicemail to play. */
-    public void setVoicemailUri(Uri voicemailUri, boolean startPlaying) {
-        mPresenter.setVoicemailUri(voicemailUri, startPlaying);
-    }
-
     private MediaPlayerProxy createMediaPlayer(ExecutorService executorService) {
         return VariableSpeed.createVariableSpeed(executorService);
     }
diff --git a/src/com/android/contacts/voicemail/VoicemailPlaybackPresenter.java b/src/com/android/contacts/voicemail/VoicemailPlaybackPresenter.java
index 5e7b707..0e7470a 100644
--- a/src/com/android/contacts/voicemail/VoicemailPlaybackPresenter.java
+++ b/src/com/android/contacts/voicemail/VoicemailPlaybackPresenter.java
@@ -119,13 +119,18 @@
     private final MediaPlayerProxy mPlayer;
     private final PositionUpdater mPositionUpdater;
 
-    /** Voicemail uri to play, will be set with a call to {@link #setVoicemailUri(Uri, boolean)}. */
-    private Uri mVoicemailUri;
+    /** Voicemail uri to play. */
+    private final Uri mVoicemailUri;
+    /** Start playing in onCreate iff this is true. */
+    private final boolean mStartPlayingImmediately;
 
     public VoicemailPlaybackPresenter(PlaybackView view, MediaPlayerProxy player,
-            ScheduledExecutorService executorService) {
+            Uri voicemailUri, ScheduledExecutorService executorService,
+            boolean startPlayingImmediately) {
         mView = view;
         mPlayer = player;
+        mVoicemailUri = voicemailUri;
+        mStartPlayingImmediately = startPlayingImmediately;
         mPositionUpdater = new PositionUpdater(executorService, SLIDER_UPDATE_PERIOD_MILLIS);
     }
 
@@ -141,6 +146,9 @@
         mView.setRateIncreaseButtonListener(createRateIncreaseListener());
         mView.setClipPosition(0, 0);
         mView.playbackStopped();
+        if (mStartPlayingImmediately) {
+            resetPrepareStartPlaying(0);
+        }
         // TODO: Now I'm ignoring the bundle, when previously I was checking for contains against
         // the PAUSED_STATE_KEY, and CLIP_POSITION_KEY.
     }
@@ -157,13 +165,6 @@
         mPlayer.release();
     }
 
-    public void setVoicemailUri(Uri voicemailUri, boolean startPlaying) {
-        mVoicemailUri = voicemailUri;
-        if (startPlaying) {
-            resetPrepareStartPlaying(0);
-        }
-    }
-
     private class MediaPlayerErrorListener implements MediaPlayer.OnErrorListener {
         @Override
         public boolean onError(MediaPlayer mp, int what, int extra) {
@@ -334,7 +335,13 @@
         private final Runnable mSetClipPostitionRunnable = new Runnable() {
             @Override
             public void run() {
-                mView.setClipPosition(mPlayer.getCurrentPosition(), mDuration.get());
+                int currentPosition = 0;
+                synchronized (mLock) {
+                    if (mScheduledFuture != null) {
+                        currentPosition = mPlayer.getCurrentPosition();
+                    }
+                }
+                mView.setClipPosition(currentPosition, mDuration.get());
             }
         };
 
@@ -345,11 +352,7 @@
 
         @Override
         public void run() {
-            synchronized (mLock) {
-                if (mScheduledFuture != null) {
-                    mView.runOnUiThread(mSetClipPostitionRunnable);
-                }
-            }
+            mView.runOnUiThread(mSetClipPostitionRunnable);
         }
 
         public void startUpdating(int beginPosition, int endPosition) {