Improve speakerphone setting.

+ Persist speakerphone setting across resume/pause.
+ Allow toggling speakerphone before voicemail is played or while
loading.

Bug: 23566924
Bug: 23716100
Change-Id: Icc7342be85bd6df0f4678134b222f2200d3fc56d
diff --git a/res/layout/voicemail_playback_layout.xml b/res/layout/voicemail_playback_layout.xml
index 555d201..54493f1 100644
--- a/res/layout/voicemail_playback_layout.xml
+++ b/res/layout/voicemail_playback_layout.xml
@@ -71,7 +71,7 @@
 
                 <ImageButton android:id="@+id/playback_speakerphone"
                     style="@style/VoicemailPlaybackLayoutButtonStyle"
-                    android:src="@drawable/ic_speakerphone_on"
+                    android:src="@drawable/ic_volume_down_24dp"
                     android:tint="@color/voicemail_icon_tint"
                     android:contentDescription="@string/description_playback_speakerphone" />
 
diff --git a/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java b/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java
index 69c075f..133da36 100644
--- a/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java
+++ b/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java
@@ -159,7 +159,7 @@
         @Override
         public void onClick(View v) {
             if (mPresenter != null) {
-                onSpeakerphoneOn(!mPresenter.isSpeakerphoneOn());
+                mPresenter.toggleSpeakerphone();
             }
         }
     };
@@ -286,10 +286,6 @@
 
         mStartStopButton.setImageResource(R.drawable.ic_pause);
 
-        if (mPresenter != null) {
-            onSpeakerphoneOn(mPresenter.isSpeakerphoneOn());
-        }
-
         if (mPositionUpdater != null) {
             mPositionUpdater.stopUpdating();
             mPositionUpdater = null;
@@ -321,10 +317,6 @@
     }
 
     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.
@@ -373,7 +365,6 @@
     @Override
     public void disableUiElements() {
         mStartStopButton.setEnabled(false);
-        mPlaybackSpeakerphone.setEnabled(false);
         mPlaybackSeek.setProgress(0);
         mPlaybackSeek.setEnabled(false);
 
@@ -384,7 +375,6 @@
     @Override
     public void enableUiElements() {
         mStartStopButton.setEnabled(true);
-        mPlaybackSpeakerphone.setEnabled(true);
         mPlaybackSeek.setEnabled(true);
 
         mPositionText.setVisibility(View.VISIBLE);
diff --git a/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java b/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
index 3f5a489..e6ab267 100644
--- a/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
+++ b/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
@@ -60,7 +60,7 @@
  * {@link CallLogFragment} and {@link CallLogAdapter}.
  * <p>
  * This controls a single {@link com.android.dialer.voicemail.VoicemailPlaybackLayout}. A single
- * instance can be reused for different such layouts, using {@link #setVoicemailPlaybackView}. This
+ * instance can be reused for different such layouts, using {@link #setPlaybackView}. This
  * is to facilitate reuse across different voicemail call log entries.
  * <p>
  * This class is not thread safe. The thread policy for this class is thread-confinement, all calls
@@ -120,6 +120,8 @@
     // If present in the saved instance bundle, indicates where to set the playback slider.
     private static final String CLIP_POSITION_KEY =
             VoicemailPlaybackPresenter.class.getName() + ".CLIP_POSITION_KEY";
+    private static final String IS_SPEAKERPHONE_ON_KEY =
+            VoicemailPlaybackPresenter.class.getName() + ".IS_SPEAKER_PHONE_ON";
 
     /**
      * The most recently cached duration. We cache this since we don't want to keep requesting it
@@ -141,6 +143,7 @@
     // MediaPlayer crashes on some method calls if not prepared but does not have a method which
     // exposes its prepared state. Store this locally, so we can check and prevent crashes.
     private boolean mIsPrepared;
+    private boolean mIsSpeakerphoneOn;
 
     private boolean mShouldResumePlaybackAfterSeeking;
     private int mInitialOrientation;
@@ -211,6 +214,7 @@
             mIsPrepared = savedInstanceState.getBoolean(IS_PREPARED_KEY);
             mPosition = savedInstanceState.getInt(CLIP_POSITION_KEY, 0);
             mIsPlaying = savedInstanceState.getBoolean(IS_PLAYING_STATE_KEY, false);
+            mIsSpeakerphoneOn = savedInstanceState.getBoolean(IS_SPEAKERPHONE_ON_KEY, false);
         }
 
         if (mMediaPlayer == null) {
@@ -228,6 +232,7 @@
             outState.putBoolean(IS_PREPARED_KEY, mIsPrepared);
             outState.putInt(CLIP_POSITION_KEY, mView.getDesiredClipPosition());
             outState.putBoolean(IS_PLAYING_STATE_KEY, mIsPlaying);
+            outState.putBoolean(IS_SPEAKERPHONE_ON_KEY, mIsSpeakerphoneOn);
         }
     }
 
@@ -239,16 +244,21 @@
         mView = view;
         mView.setPresenter(this, voicemailUri);
 
+        // Handles cases where the same entry is binded again when scrolling in list, or where
+        // the MediaPlayer was retained after an orientation change.
         if (mMediaPlayer != null && mIsPrepared && voicemailUri.equals(mVoicemailUri)) {
-            // Handles case where MediaPlayer was retained after an orientation change.
             onPrepared(mMediaPlayer);
-            mView.onSpeakerphoneOn(isSpeakerphoneOn());
         } else {
             if (!voicemailUri.equals(mVoicemailUri)) {
+                mVoicemailUri = voicemailUri;
                 mPosition = 0;
+                // Default to earpiece.
+                setSpeakerphoneOn(false);
+            } else {
+                // Update the view to the current speakerphone state.
+                mView.onSpeakerphoneOn(mIsSpeakerphoneOn);
             }
 
-            mVoicemailUri = voicemailUri;
             mDuration.set(0);
 
             if (startPlayingImmediately) {
@@ -258,9 +268,6 @@
                 mIsPlaying = startPlayingImmediately;
                 checkForContent();
             }
-
-            // Default to earpiece.
-            mView.onSpeakerphoneOn(false);
         }
     }
 
@@ -595,7 +602,6 @@
             // If we haven't downloaded the voicemail yet, attempt to download it.
             checkForContent();
             mIsPlaying = true;
-
             return;
         }
 
@@ -616,6 +622,7 @@
                     throw new RejectedExecutionException("Could not capture audio focus.");
                 }
 
+                setSpeakerphoneOn(mIsSpeakerphoneOn);
                 // Can throw RejectedExecutionException.
                 mMediaPlayer.start();
             } catch (RejectedExecutionException e) {
@@ -625,11 +632,6 @@
 
         Log.d(TAG, "Resumed playback at " + mPosition + ".");
         mView.onPlaybackStarted(mDuration.get(), getScheduledExecutorServiceInstance());
-        if (isSpeakerphoneOn()) {
-            mActivity.getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
-        } else {
-            enableProximitySensor();
-        }
     }
 
     /**
@@ -681,7 +683,7 @@
     }
 
     private void enableProximitySensor() {
-        if (mProximityWakeLock == null || isSpeakerphoneOn() || !mIsPrepared
+        if (mProximityWakeLock == null || mIsSpeakerphoneOn || !mIsPrepared
                 || mMediaPlayer == null || !mMediaPlayer.isPlaying()) {
             return;
         }
@@ -707,24 +709,32 @@
         }
     }
 
-    public void setSpeakerphoneOn(boolean on) {
-        mAudioManager.setSpeakerphoneOn(on);
-
-        if (on) {
-            disableProximitySensor(false /* waitForFarState */);
-            if (mIsPrepared && mMediaPlayer != null && mMediaPlayer.isPlaying()) {
-                mActivity.getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
-            }
-        } else {
-            enableProximitySensor();
-            if (mActivity != null) {
-                mActivity.getWindow().clearFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
-            }
-        }
+    public void toggleSpeakerphone() {
+        setSpeakerphoneOn(!mIsSpeakerphoneOn);
     }
 
-    public boolean isSpeakerphoneOn() {
-        return mAudioManager.isSpeakerphoneOn();
+    private void setSpeakerphoneOn(boolean on) {
+        mView.onSpeakerphoneOn(on);
+        if (mIsSpeakerphoneOn == on) {
+            return;
+        }
+
+        mIsSpeakerphoneOn = on;
+        mAudioManager.setSpeakerphoneOn(mIsSpeakerphoneOn);
+
+        if (mIsPlaying) {
+            if (on) {
+                disableProximitySensor(false /* waitForFarState */);
+                if (mIsPrepared && mMediaPlayer != null && mMediaPlayer.isPlaying()) {
+                    mActivity.getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
+                }
+            } else {
+                enableProximitySensor();
+                if (mActivity != null) {
+                    mActivity.getWindow().clearFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
+                }
+            }
+        }
     }
 
     public void setOnVoicemailDeletedListener(OnVoicemailDeletedListener listener) {
@@ -767,4 +777,9 @@
     public boolean isPlaying() {
         return mIsPlaying;
     }
+
+    @VisibleForTesting
+    public boolean isSpeakerphoneOn() {
+        return mIsSpeakerphoneOn;
+    }
 }
diff --git a/tests/src/com/android/dialer/voicemail/VoicemailPlaybackTest.java b/tests/src/com/android/dialer/voicemail/VoicemailPlaybackTest.java
index b9c70d3..6c7cf77 100644
--- a/tests/src/com/android/dialer/voicemail/VoicemailPlaybackTest.java
+++ b/tests/src/com/android/dialer/voicemail/VoicemailPlaybackTest.java
@@ -143,8 +143,7 @@
         mFakeAsyncTaskExecutor.runTask(CHECK_FOR_CONTENT);
         getInstrumentation().waitForIdleSync();
 
-        // Force the speakerphone to false to start.
-        mPresenter.setSpeakerphoneOn(false);
+        // Check that the speakerphone is false to start.
         assertFalse(mPresenter.isSpeakerphoneOn());
 
         View speakerphoneButton = mLayout.findViewById(R.id.playback_speakerphone);