Add 0.5 second delay to set stream or play volume ringtone

If get Ring/Alarm/Media stream volume right after stop call volume,
it will return the volume of the earpiece then cause UI incorrect.
It is because the call volume is from the earpiece and the
alarm/ring/media is from the speaker.
This CL adds 0.5 second delay when play volume or set stream
to avoid get wrong volume.

- Add onStartTrackingTouch() callback to stop the ringtone when
  another seek bar is adjusted.
- Add 0.5 second delay to set stream or play volume ringtone.

Bug: 80194851
Test: build, adjust call volume then immediately adjust ring volume
Change-Id: I06c5e84af60f0d77daffabb4a22644f43a9cf54d
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index 0cdad9f..400b312 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -46,6 +46,8 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.SomeArgs;
 
+import java.util.concurrent.TimeUnit;
+
 /**
  * Turns a {@link SeekBar} into a volume control.
  * @hide
@@ -64,9 +66,15 @@
         void onSampleStarting(SeekBarVolumizer sbv);
         void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch);
         void onMuted(boolean muted, boolean zenMuted);
+        /**
+         * Callback reporting that the seek bar is start tracking.
+         * @param sbv - The seek bar that start tracking
+         */
+        void onStartTrackingTouch(SeekBarVolumizer sbv);
     }
 
     private static final int MSG_GROUP_VOLUME_CHANGED = 1;
+    private static long sStopVolumeTime = 0L;
     private final Handler mVolumeHandler = new VolumeHandler();
     private AudioAttributes mAttributes;
     private int mVolumeGroupId;
@@ -126,6 +134,9 @@
     private static final int MSG_STOP_SAMPLE = 2;
     private static final int MSG_INIT_SAMPLE = 3;
     private static final int CHECK_RINGTONE_PLAYBACK_DELAY_MS = 1000;
+    private static final long SET_STREAM_VOLUME_DELAY_MS = TimeUnit.MILLISECONDS.toMillis(500);
+    private static final long START_SAMPLE_DELAY_MS = TimeUnit.MILLISECONDS.toMillis(500);
+    private static final long DURATION_TO_START_DELAYING = TimeUnit.MILLISECONDS.toMillis(2000);
 
     private NotificationManager.Policy mNotificationPolicy;
     private boolean mAllowAlarms;
@@ -314,7 +325,29 @@
         if (mHandler == null) return;
         mHandler.removeMessages(MSG_START_SAMPLE);
         mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_SAMPLE),
-                isSamplePlaying() ? CHECK_RINGTONE_PLAYBACK_DELAY_MS : 0);
+                isSamplePlaying() ? CHECK_RINGTONE_PLAYBACK_DELAY_MS
+                        : isDelay() ? START_SAMPLE_DELAY_MS : 0);
+    }
+
+    // After stop volume it needs to add a small delay when playing volume or set stream.
+    // It is because the call volume is from the earpiece and the alarm/ring/media
+    // is from the speaker. If play the alarm volume or set alarm stream right after stop
+    // call volume, the alarm volume on earpiece is returned then cause the volume value incorrect.
+    // It needs a small delay after stop call volume to get alarm volume on speaker.
+    // e.g. : If the ring volume has adjusted right after call volume stopped in 2 second
+    // then delay 0.5 second to set stream or play volume ringtone.
+    private boolean isDelay() {
+        final long durationTime = java.lang.System.currentTimeMillis() - sStopVolumeTime;
+        return durationTime >= 0 && durationTime < DURATION_TO_START_DELAYING;
+    }
+
+    private void setStopVolumeTime() {
+        // set the time of stop volume
+        if ((mStreamType == AudioManager.STREAM_VOICE_CALL
+                || mStreamType == AudioManager.STREAM_RING
+                || mStreamType == AudioManager.STREAM_ALARM)) {
+            sStopVolumeTime = java.lang.System.currentTimeMillis();
+        }
     }
 
     private void onStartSample() {
@@ -341,6 +374,7 @@
 
     private void postStopSample() {
         if (mHandler == null) return;
+        setStopVolumeTime();
         // remove pending delayed start messages
         mHandler.removeMessages(MSG_START_SAMPLE);
         mHandler.removeMessages(MSG_STOP_SAMPLE);
@@ -404,10 +438,15 @@
         // Do the volume changing separately to give responsive UI
         mLastProgress = progress;
         mHandler.removeMessages(MSG_SET_STREAM_VOLUME);
-        mHandler.sendMessage(mHandler.obtainMessage(MSG_SET_STREAM_VOLUME));
+        mHandler.removeMessages(MSG_START_SAMPLE);
+        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SET_STREAM_VOLUME),
+                isDelay() ? SET_STREAM_VOLUME_DELAY_MS : 0);
     }
 
     public void onStartTrackingTouch(SeekBar seekBar) {
+        if (mCallback != null) {
+            mCallback.onStartTrackingTouch(this);
+        }
     }
 
     public void onStopTrackingTouch(SeekBar seekBar) {
@@ -539,7 +578,7 @@
             if (AudioManager.VOLUME_CHANGED_ACTION.equals(action)) {
                 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
                 int streamValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
-                if (hasAudioProductStrategies()) {
+                if (hasAudioProductStrategies() && !isDelay()) {
                     updateVolumeSlider(streamType, streamValue);
                 }
             } else if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) {
@@ -551,7 +590,7 @@
                 }
             } else if (AudioManager.STREAM_DEVICES_CHANGED_ACTION.equals(action)) {
                 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
-                if (hasAudioProductStrategies()) {
+                if (hasAudioProductStrategies() && !isDelay()) {
                     int streamVolume = mAudioManager.getStreamVolume(streamType);
                     updateVolumeSlider(streamType, streamVolume);
                 } else {
@@ -559,7 +598,9 @@
                     if (volumeGroup != AudioVolumeGroup.DEFAULT_VOLUME_GROUP
                             && volumeGroup == mVolumeGroupId) {
                         int streamVolume = mAudioManager.getStreamVolume(streamType);
-                        updateVolumeSlider(streamType, streamVolume);
+                        if (!isDelay()) {
+                            updateVolumeSlider(streamType, streamVolume);
+                        }
                     }
                 }
             } else if (NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED.equals(action)) {
diff --git a/core/java/android/preference/VolumePreference.java b/core/java/android/preference/VolumePreference.java
index 6eb524a..563bc46 100644
--- a/core/java/android/preference/VolumePreference.java
+++ b/core/java/android/preference/VolumePreference.java
@@ -176,6 +176,11 @@
     }
 
     @Override
+    public void onStartTrackingTouch(SeekBarVolumizer sbv) {
+        // noop
+    }
+
+    @Override
     protected Parcelable onSaveInstanceState() {
         final Parcelable superState = super.onSaveInstanceState();
         if (isPersistent()) {