Reimplement voice note proximity locking.
Proximity lock was tied to the VoiceNotePlaybackService instead of to the Activity, and it made for some strange code decisions. This rewrite now ties locking to the activity, where it should have been in the first place, and hopefully solves a few proximity / playback bugs on the way. In addition, it conforms to SRP better as it will send a command to the player to change the audio attributes as necessary instead of directly operating on a player instance.
This commit is contained in:
parent
2d5492ffac
commit
c78e283084
6 changed files with 290 additions and 153 deletions
|
@ -57,6 +57,7 @@ public class VoiceNoteMediaController implements DefaultLifecycleObserver {
|
|||
private ProgressEventHandler progressEventHandler;
|
||||
private MutableLiveData<VoiceNotePlaybackState> voiceNotePlaybackState = new MutableLiveData<>(VoiceNotePlaybackState.NONE);
|
||||
private LiveData<Optional<VoiceNotePlayerView.State>> voiceNotePlayerViewState;
|
||||
private VoiceNoteProximityWakeLockManager voiceNoteProximityWakeLockManager;
|
||||
|
||||
private final MediaControllerCompatCallback mediaControllerCompatCallback = new MediaControllerCompatCallback();
|
||||
|
||||
|
@ -275,6 +276,9 @@ public class VoiceNoteMediaController implements DefaultLifecycleObserver {
|
|||
}
|
||||
}
|
||||
|
||||
cleanUpOldProximityWakeLockManager();
|
||||
voiceNoteProximityWakeLockManager = new VoiceNoteProximityWakeLockManager(activity, mediaController);
|
||||
|
||||
mediaController.registerCallback(mediaControllerCompatCallback);
|
||||
|
||||
mediaControllerCompatCallback.onPlaybackStateChanged(mediaController.getPlaybackState());
|
||||
|
@ -282,6 +286,26 @@ public class VoiceNoteMediaController implements DefaultLifecycleObserver {
|
|||
Log.w(TAG, "onConnected: Failed to set media controller", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionSuspended() {
|
||||
Log.d(TAG, "Voice note MediaBrowser connection suspended.");
|
||||
cleanUpOldProximityWakeLockManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionFailed() {
|
||||
Log.d(TAG, "Voice note MediaBrowser connection failed.");
|
||||
cleanUpOldProximityWakeLockManager();
|
||||
}
|
||||
|
||||
private void cleanUpOldProximityWakeLockManager() {
|
||||
if (voiceNoteProximityWakeLockManager != null) {
|
||||
Log.d(TAG, "Session reconnected, cleaning up old wake lock manager");
|
||||
voiceNoteProximityWakeLockManager.unregisterCallbacksAndRelease();
|
||||
voiceNoteProximityWakeLockManager = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean canExtractPlaybackInformationFromMetadata(@Nullable MediaMetadataCompat mediaMetadataCompat) {
|
||||
|
|
|
@ -1,15 +1,20 @@
|
|||
package org.thoughtcrime.securesms.components.voice
|
||||
|
||||
import android.media.AudioManager
|
||||
import android.os.Bundle
|
||||
import android.os.ResultReceiver
|
||||
import com.google.android.exoplayer2.C
|
||||
import com.google.android.exoplayer2.PlaybackParameters
|
||||
import com.google.android.exoplayer2.Player
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer
|
||||
import com.google.android.exoplayer2.audio.AudioAttributes
|
||||
import com.google.android.exoplayer2.ext.mediasession.DefaultPlaybackController
|
||||
import com.google.android.exoplayer2.util.Util
|
||||
|
||||
class VoiceNotePlaybackController(private val voiceNotePlaybackParameters: VoiceNotePlaybackParameters) : DefaultPlaybackController() {
|
||||
|
||||
override fun getCommands(): Array<String> {
|
||||
return arrayOf(VoiceNotePlaybackService.ACTION_NEXT_PLAYBACK_SPEED)
|
||||
return arrayOf(VoiceNotePlaybackService.ACTION_NEXT_PLAYBACK_SPEED, VoiceNotePlaybackService.ACTION_SET_AUDIO_STREAM)
|
||||
}
|
||||
|
||||
override fun onCommand(player: Player, command: String, extras: Bundle?, cb: ResultReceiver?) {
|
||||
|
@ -18,6 +23,24 @@ class VoiceNotePlaybackController(private val voiceNotePlaybackParameters: Voice
|
|||
|
||||
player.playbackParameters = PlaybackParameters(speed)
|
||||
voiceNotePlaybackParameters.setSpeed(speed)
|
||||
} else if (command == VoiceNotePlaybackService.ACTION_SET_AUDIO_STREAM) {
|
||||
val newStreamType: Int = extras?.getInt(VoiceNotePlaybackService.ACTION_SET_AUDIO_STREAM, AudioManager.STREAM_MUSIC) ?: AudioManager.STREAM_MUSIC
|
||||
|
||||
val currentStreamType = Util.getStreamTypeForAudioUsage((player as SimpleExoPlayer).audioAttributes.usage)
|
||||
if (newStreamType != currentStreamType) {
|
||||
val attributes = when (newStreamType) {
|
||||
AudioManager.STREAM_MUSIC -> AudioAttributes.Builder().setContentType(C.CONTENT_TYPE_MUSIC).setUsage(C.USAGE_MEDIA).build()
|
||||
AudioManager.STREAM_VOICE_CALL -> AudioAttributes.Builder().setContentType(C.CONTENT_TYPE_SPEECH).setUsage(C.USAGE_VOICE_COMMUNICATION).build()
|
||||
else -> throw AssertionError()
|
||||
}
|
||||
|
||||
player.playWhenReady = false
|
||||
player.audioAttributes = attributes
|
||||
|
||||
if (newStreamType == AudioManager.STREAM_VOICE_CALL) {
|
||||
player.playWhenReady = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ import java.util.List;
|
|||
public class VoiceNotePlaybackService extends MediaBrowserServiceCompat {
|
||||
|
||||
public static final String ACTION_NEXT_PLAYBACK_SPEED = "org.thoughtcrime.securesms.components.voice.VoiceNotePlaybackService.action.next_playback_speed";
|
||||
public static final String ACTION_SET_AUDIO_STREAM = "org.thoughtcrime.securesms.components.voice.VoiceNotePlaybackService.action.set_audio_stream";
|
||||
|
||||
private static final String TAG = Log.tag(VoiceNotePlaybackService.class);
|
||||
private static final String EMPTY_ROOT_ID = "empty-root-id";
|
||||
|
@ -76,7 +77,6 @@ public class VoiceNotePlaybackService extends MediaBrowserServiceCompat {
|
|||
private VoiceNoteNotificationManager voiceNoteNotificationManager;
|
||||
private VoiceNoteQueueDataAdapter queueDataAdapter;
|
||||
private VoiceNotePlaybackPreparer voiceNotePlaybackPreparer;
|
||||
private VoiceNoteProximityManager voiceNoteProximityManager;
|
||||
private boolean isForegroundService;
|
||||
private VoiceNotePlaybackParameters voiceNotePlaybackParameters;
|
||||
|
||||
|
@ -109,7 +109,6 @@ public class VoiceNotePlaybackService extends MediaBrowserServiceCompat {
|
|||
AttachmentMediaSourceFactory mediaSourceFactory = new AttachmentMediaSourceFactory(this);
|
||||
|
||||
voiceNotePlaybackPreparer = new VoiceNotePlaybackPreparer(this, player, queueDataAdapter, mediaSourceFactory, voiceNotePlaybackParameters);
|
||||
voiceNoteProximityManager = new VoiceNoteProximityManager(this, player, queueDataAdapter);
|
||||
|
||||
mediaSession.setPlaybackState(stateBuilder.build());
|
||||
|
||||
|
@ -171,15 +170,12 @@ public class VoiceNotePlaybackService extends MediaBrowserServiceCompat {
|
|||
if (!playWhenReady) {
|
||||
stopForeground(false);
|
||||
becomingNoisyReceiver.unregister();
|
||||
voiceNoteProximityManager.onPlayerEnded();
|
||||
} else {
|
||||
sendViewedReceiptForCurrentWindowIndex();
|
||||
becomingNoisyReceiver.register();
|
||||
voiceNoteProximityManager.onPlayerReady();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
voiceNoteProximityManager.onPlayerEnded();
|
||||
becomingNoisyReceiver.unregister();
|
||||
voiceNoteNotificationManager.hideNotification();
|
||||
}
|
||||
|
@ -219,7 +215,6 @@ public class VoiceNotePlaybackService extends MediaBrowserServiceCompat {
|
|||
@Override
|
||||
public void onPlayerError(ExoPlaybackException error) {
|
||||
Log.w(TAG, "ExoPlayer error occurred:", error);
|
||||
voiceNoteProximityManager.onPlayerError();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,146 +0,0 @@
|
|||
package org.thoughtcrime.securesms.components.voice;
|
||||
|
||||
import android.content.Context;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
import android.os.PowerManager;
|
||||
import android.support.v4.media.MediaDescriptionCompat;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.audio.AudioAttributes;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.util.ServiceUtil;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
class VoiceNoteProximityManager implements SensorEventListener {
|
||||
|
||||
private static final String TAG = Log.tag(VoiceNoteProximityManager.class);
|
||||
|
||||
private static final float PROXIMITY_THRESHOLD = 5f;
|
||||
|
||||
private final SimpleExoPlayer player;
|
||||
private final AudioManager audioManager;
|
||||
private final SensorManager sensorManager;
|
||||
private final Sensor proximitySensor;
|
||||
private final PowerManager.WakeLock wakeLock;
|
||||
private final VoiceNoteQueueDataAdapter queueDataAdapter;
|
||||
|
||||
private long startTime;
|
||||
|
||||
VoiceNoteProximityManager(@NonNull Context context,
|
||||
@NonNull SimpleExoPlayer player,
|
||||
@NonNull VoiceNoteQueueDataAdapter queueDataAdapter)
|
||||
{
|
||||
this.player = player;
|
||||
this.audioManager = ServiceUtil.getAudioManager(context);
|
||||
this.sensorManager = ServiceUtil.getSensorManager(context);
|
||||
this.proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
|
||||
this.queueDataAdapter = queueDataAdapter;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
this.wakeLock = ServiceUtil.getPowerManager(context).newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, TAG);
|
||||
} else {
|
||||
this.wakeLock = null;
|
||||
}
|
||||
}
|
||||
|
||||
void onPlayerReady() {
|
||||
startTime = System.currentTimeMillis();
|
||||
sensorManager.registerListener(this, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL);
|
||||
}
|
||||
|
||||
void onPlayerEnded() {
|
||||
sensorManager.unregisterListener(this);
|
||||
|
||||
if (wakeLock != null && wakeLock.isHeld() && Build.VERSION.SDK_INT >= 21) {
|
||||
wakeLock.release(PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY);
|
||||
}
|
||||
}
|
||||
|
||||
void onPlayerError() {
|
||||
onPlayerEnded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
if (event.sensor.getType() != Sensor.TYPE_PROXIMITY || player.getPlaybackState() != Player.STATE_READY) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int desiredStreamType;
|
||||
if (event.values[0] < PROXIMITY_THRESHOLD && event.values[0] != proximitySensor.getMaximumRange()) {
|
||||
desiredStreamType = AudioManager.STREAM_VOICE_CALL;
|
||||
} else {
|
||||
desiredStreamType = AudioManager.STREAM_MUSIC;
|
||||
}
|
||||
|
||||
final int currentStreamType = Util.getStreamTypeForAudioUsage(player.getAudioAttributes().usage);
|
||||
|
||||
final long threadId;
|
||||
final int windowIndex = player.getCurrentWindowIndex();
|
||||
|
||||
if (queueDataAdapter.isEmpty() || windowIndex == C.INDEX_UNSET) {
|
||||
threadId = -1;
|
||||
} else {
|
||||
MediaDescriptionCompat mediaDescriptionCompat = queueDataAdapter.getMediaDescription(windowIndex);
|
||||
|
||||
if (mediaDescriptionCompat.getExtras() == null) {
|
||||
threadId = -1;
|
||||
} else {
|
||||
threadId = mediaDescriptionCompat.getExtras().getLong(VoiceNoteMediaDescriptionCompatFactory.EXTRA_THREAD_ID, -1);
|
||||
}
|
||||
}
|
||||
|
||||
if (desiredStreamType == AudioManager.STREAM_VOICE_CALL &&
|
||||
desiredStreamType != currentStreamType &&
|
||||
!audioManager.isWiredHeadsetOn() &&
|
||||
threadId != -1 &&
|
||||
ApplicationDependencies.getMessageNotifier().getVisibleThread() == threadId)
|
||||
{
|
||||
if (wakeLock != null && !wakeLock.isHeld()) {
|
||||
wakeLock.acquire(TimeUnit.MINUTES.toMillis(30));
|
||||
}
|
||||
|
||||
player.setPlayWhenReady(false);
|
||||
player.setAudioAttributes(new AudioAttributes.Builder()
|
||||
.setContentType(C.CONTENT_TYPE_SPEECH)
|
||||
.setUsage(C.USAGE_VOICE_COMMUNICATION)
|
||||
.build());
|
||||
player.setPlayWhenReady(true);
|
||||
|
||||
startTime = System.currentTimeMillis();
|
||||
} else if (desiredStreamType == AudioManager.STREAM_MUSIC &&
|
||||
desiredStreamType != currentStreamType &&
|
||||
System.currentTimeMillis() - startTime > 500)
|
||||
{
|
||||
if (wakeLock != null) {
|
||||
if (wakeLock.isHeld()) {
|
||||
wakeLock.release();
|
||||
}
|
||||
|
||||
player.setPlayWhenReady(false);
|
||||
player.setAudioAttributes(new AudioAttributes.Builder()
|
||||
.setContentType(C.CONTENT_TYPE_MUSIC)
|
||||
.setUsage(C.USAGE_MEDIA)
|
||||
.build(),
|
||||
true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
package org.thoughtcrime.securesms.components.voice
|
||||
|
||||
import android.hardware.Sensor
|
||||
import android.hardware.SensorEvent
|
||||
import android.hardware.SensorEventListener
|
||||
import android.hardware.SensorManager
|
||||
import android.media.AudioManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.PowerManager
|
||||
import android.support.v4.media.session.MediaControllerCompat
|
||||
import android.support.v4.media.session.PlaybackStateCompat
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.util.ServiceUtil
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
private val TAG = Log.tag(VoiceNoteProximityWakeLockManager::class.java)
|
||||
private const val PROXIMITY_THRESHOLD = 5f
|
||||
|
||||
/**
|
||||
* Manages the WakeLock while a VoiceNote is playing back in the target activity.
|
||||
*/
|
||||
class VoiceNoteProximityWakeLockManager(
|
||||
private val activity: AppCompatActivity,
|
||||
private val mediaController: MediaControllerCompat
|
||||
) : DefaultLifecycleObserver {
|
||||
|
||||
private val wakeLock: PowerManager.WakeLock? = if (Build.VERSION.SDK_INT >= 21) {
|
||||
ServiceUtil.getPowerManager(activity).newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, TAG)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
private val sensorManager: SensorManager = ServiceUtil.getSensorManager(activity)
|
||||
private val proximitySensor: Sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)
|
||||
|
||||
private val mediaControllerCallback = MediaControllerCallback()
|
||||
private val hardwareSensorEventListener = HardwareSensorEventListener()
|
||||
|
||||
private var startTime: Long = -1
|
||||
|
||||
init {
|
||||
activity.lifecycle.addObserver(this)
|
||||
}
|
||||
|
||||
override fun onResume(owner: LifecycleOwner) {
|
||||
mediaController.registerCallback(mediaControllerCallback)
|
||||
}
|
||||
|
||||
override fun onPause(owner: LifecycleOwner) {
|
||||
unregisterCallbacksAndRelease()
|
||||
}
|
||||
|
||||
fun unregisterCallbacksAndRelease() {
|
||||
mediaController.unregisterCallback(mediaControllerCallback)
|
||||
cleanUpWakeLock()
|
||||
}
|
||||
|
||||
private fun isActivityResumed() = activity.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)
|
||||
|
||||
private fun isPlayerActive() = mediaController.playbackState.state == PlaybackStateCompat.STATE_BUFFERING ||
|
||||
mediaController.playbackState.state == PlaybackStateCompat.STATE_PLAYING
|
||||
|
||||
private fun cleanUpWakeLock() {
|
||||
startTime = -1L
|
||||
sensorManager.unregisterListener(hardwareSensorEventListener)
|
||||
|
||||
if (wakeLock?.isHeld == true) {
|
||||
wakeLock.release()
|
||||
}
|
||||
|
||||
sendNewStreamTypeToPlayer(AudioManager.STREAM_MUSIC)
|
||||
}
|
||||
|
||||
private fun sendNewStreamTypeToPlayer(newStreamType: Int) {
|
||||
val params = Bundle()
|
||||
params.putInt(VoiceNotePlaybackService.ACTION_SET_AUDIO_STREAM, newStreamType)
|
||||
mediaController.sendCommand(VoiceNotePlaybackService.ACTION_SET_AUDIO_STREAM, params, null)
|
||||
}
|
||||
|
||||
inner class MediaControllerCallback : MediaControllerCompat.Callback() {
|
||||
override fun onPlaybackStateChanged(state: PlaybackStateCompat) {
|
||||
if (!isActivityResumed()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (isPlayerActive()) {
|
||||
if (startTime == -1L) {
|
||||
startTime = System.currentTimeMillis()
|
||||
sensorManager.registerListener(hardwareSensorEventListener, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL)
|
||||
}
|
||||
} else {
|
||||
cleanUpWakeLock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inner class HardwareSensorEventListener : SensorEventListener {
|
||||
override fun onSensorChanged(event: SensorEvent) {
|
||||
if (startTime == -1L ||
|
||||
System.currentTimeMillis() - startTime <= 500 ||
|
||||
!isActivityResumed() ||
|
||||
!isPlayerActive() ||
|
||||
event.sensor.type != Sensor.TYPE_PROXIMITY
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
val newStreamType = if (event.values[0] < PROXIMITY_THRESHOLD && event.values[0] != proximitySensor.maximumRange) {
|
||||
AudioManager.STREAM_VOICE_CALL
|
||||
} else {
|
||||
AudioManager.STREAM_MUSIC
|
||||
}
|
||||
|
||||
sendNewStreamTypeToPlayer(newStreamType)
|
||||
|
||||
if (newStreamType == AudioManager.STREAM_VOICE_CALL) {
|
||||
if (wakeLock?.isHeld == false) {
|
||||
wakeLock.acquire(TimeUnit.MINUTES.toMillis(30))
|
||||
}
|
||||
|
||||
startTime = System.currentTimeMillis()
|
||||
} else {
|
||||
if (wakeLock?.isHeld == true) {
|
||||
wakeLock.release()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) = Unit
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
package org.thoughtcrime.securesms.components.voice
|
||||
|
||||
import android.app.Application
|
||||
import android.media.AudioManager
|
||||
import android.os.Bundle
|
||||
import android.support.v4.media.session.MediaSessionCompat
|
||||
import com.google.android.exoplayer2.C
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer
|
||||
import com.google.android.exoplayer2.audio.AudioAttributes
|
||||
import org.junit.Assert.assertArrayEquals
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.Mockito.`when`
|
||||
import org.mockito.Mockito.any
|
||||
import org.mockito.Mockito.anyBoolean
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.Mockito.verify
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(manifest = Config.NONE, application = Application::class)
|
||||
class VoiceNotePlaybackControllerTest {
|
||||
|
||||
private val mediaSessionCompat = mock(MediaSessionCompat::class.java)
|
||||
private val playbackParameters = VoiceNotePlaybackParameters(mediaSessionCompat)
|
||||
private val testSubject = VoiceNotePlaybackController(playbackParameters)
|
||||
private val mediaAudioAttributes = AudioAttributes.Builder().setContentType(C.CONTENT_TYPE_MUSIC).setUsage(C.USAGE_MEDIA).build()
|
||||
private val callAudioAttributes = AudioAttributes.Builder().setContentType(C.CONTENT_TYPE_SPEECH).setUsage(C.USAGE_VOICE_COMMUNICATION).build()
|
||||
private val player: SimpleExoPlayer = mock(SimpleExoPlayer::class.java)
|
||||
|
||||
@Test
|
||||
fun `When I getCommands, then I expect PLAYBACK_SPEED and AUDIO_STREAM`() {
|
||||
assertArrayEquals(arrayOf(VoiceNotePlaybackService.ACTION_NEXT_PLAYBACK_SPEED, VoiceNotePlaybackService.ACTION_SET_AUDIO_STREAM), testSubject.commands)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Given stream is media, When I onCommand for voice, then I expect the stream to switch to voice and continue playback`() {
|
||||
// GIVEN
|
||||
`when`(player.audioAttributes).thenReturn(mediaAudioAttributes)
|
||||
|
||||
val command = VoiceNotePlaybackService.ACTION_SET_AUDIO_STREAM
|
||||
val extras = Bundle().apply { putInt(VoiceNotePlaybackService.ACTION_SET_AUDIO_STREAM, AudioManager.STREAM_VOICE_CALL) }
|
||||
val expected = callAudioAttributes
|
||||
|
||||
// WHEN
|
||||
testSubject.onCommand(player, command, extras, null)
|
||||
|
||||
// THEN
|
||||
verify(player).playWhenReady = false
|
||||
verify(player).audioAttributes = expected
|
||||
verify(player).playWhenReady = true
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Given stream is voice, When I onCommand for media, then I expect the stream to switch to media and pause playback`() {
|
||||
// GIVEN
|
||||
`when`(player.audioAttributes).thenReturn(callAudioAttributes)
|
||||
|
||||
val command = VoiceNotePlaybackService.ACTION_SET_AUDIO_STREAM
|
||||
val extras = Bundle().apply { putInt(VoiceNotePlaybackService.ACTION_SET_AUDIO_STREAM, AudioManager.STREAM_MUSIC) }
|
||||
val expected = mediaAudioAttributes
|
||||
|
||||
// WHEN
|
||||
testSubject.onCommand(player, command, extras, null)
|
||||
|
||||
// THEN
|
||||
verify(player).playWhenReady = false
|
||||
verify(player).audioAttributes = expected
|
||||
verify(player, Mockito.never()).playWhenReady = true
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Given stream is voice, When I onCommand for voice, then I expect no change`() {
|
||||
// GIVEN
|
||||
`when`(player.audioAttributes).thenReturn(callAudioAttributes)
|
||||
|
||||
val command = VoiceNotePlaybackService.ACTION_SET_AUDIO_STREAM
|
||||
val extras = Bundle().apply { putInt(VoiceNotePlaybackService.ACTION_SET_AUDIO_STREAM, AudioManager.STREAM_VOICE_CALL) }
|
||||
|
||||
// WHEN
|
||||
testSubject.onCommand(player, command, extras, null)
|
||||
|
||||
// THEN
|
||||
verify(player, Mockito.never()).playWhenReady = anyBoolean()
|
||||
verify(player, Mockito.never()).audioAttributes = any()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Given stream is media, When I onCommand for media, then I expect no change`() {
|
||||
// GIVEN
|
||||
`when`(player.audioAttributes).thenReturn(mediaAudioAttributes)
|
||||
|
||||
val command = VoiceNotePlaybackService.ACTION_SET_AUDIO_STREAM
|
||||
val extras = Bundle().apply { putInt(VoiceNotePlaybackService.ACTION_SET_AUDIO_STREAM, AudioManager.STREAM_MUSIC) }
|
||||
|
||||
// WHEN
|
||||
testSubject.onCommand(player, command, extras, null)
|
||||
|
||||
// THEN
|
||||
verify(player, Mockito.never()).playWhenReady = anyBoolean()
|
||||
verify(player, Mockito.never()).audioAttributes = any()
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue