Support vibration-only via Ringtone player.
Bug: 261571543 Test: unit Change-Id: Ia0f327f9d3a3a61fa3b42465f8b622c8c7473257
This commit is contained in:
@@ -39,27 +39,23 @@ import java.util.Objects;
|
||||
public class LocalRingtonePlayer
|
||||
implements Ringtone.RingtonePlayer, MediaPlayer.OnCompletionListener {
|
||||
private static final String TAG = "LocalRingtonePlayer";
|
||||
private static final int VIBRATION_LOOP_DELAY_MS = 200;
|
||||
|
||||
// keep references on active Ringtones until stopped or completion listener called.
|
||||
private static final ArrayList<LocalRingtonePlayer> sActiveMediaPlayers = new ArrayList<>();
|
||||
|
||||
private final MediaPlayer mMediaPlayer;
|
||||
private final AudioAttributes mAudioAttributes;
|
||||
private final VibrationAttributes mVibrationAttributes;
|
||||
private final Ringtone.RingtonePlayer mVibrationPlayer;
|
||||
private final Ringtone.Injectables mInjectables;
|
||||
private final AudioManager mAudioManager;
|
||||
private final VolumeShaper mVolumeShaper;
|
||||
private final Vibrator mVibrator;
|
||||
private final VibrationEffect mVibrationEffect;
|
||||
private HapticGenerator mHapticGenerator;
|
||||
private boolean mStartedVibration;
|
||||
|
||||
private LocalRingtonePlayer(@NonNull MediaPlayer mediaPlayer,
|
||||
@NonNull AudioAttributes audioAttributes, @NonNull Ringtone.Injectables injectables,
|
||||
@NonNull AudioManager audioManager, @Nullable HapticGenerator hapticGenerator,
|
||||
@Nullable VolumeShaper volumeShaper, @NonNull Vibrator vibrator,
|
||||
@Nullable VibrationEffect vibrationEffect) {
|
||||
@Nullable VolumeShaper volumeShaper,
|
||||
@Nullable Ringtone.RingtonePlayer vibrationPlayer) {
|
||||
Objects.requireNonNull(mediaPlayer);
|
||||
Objects.requireNonNull(audioAttributes);
|
||||
Objects.requireNonNull(injectables);
|
||||
@@ -69,11 +65,8 @@ public class LocalRingtonePlayer
|
||||
mInjectables = injectables;
|
||||
mAudioManager = audioManager;
|
||||
mVolumeShaper = volumeShaper;
|
||||
mVibrator = vibrator;
|
||||
mVibrationEffect = vibrationEffect;
|
||||
mVibrationPlayer = vibrationPlayer;
|
||||
mHapticGenerator = hapticGenerator;
|
||||
mVibrationAttributes = (mVibrationEffect == null) ? null :
|
||||
new VibrationAttributes.Builder(audioAttributes).build();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,9 +74,11 @@ public class LocalRingtonePlayer
|
||||
* loaded in the local player.
|
||||
*/
|
||||
@Nullable
|
||||
static LocalRingtonePlayer create(@NonNull Context context,
|
||||
@NonNull AudioManager audioManager, @NonNull Vibrator vibrator, @NonNull Uri soundUri,
|
||||
static Ringtone.RingtonePlayer create(@NonNull Context context,
|
||||
@NonNull AudioManager audioManager, @NonNull Vibrator vibrator,
|
||||
@NonNull Uri soundUri,
|
||||
@NonNull AudioAttributes audioAttributes,
|
||||
boolean isVibrationOnly,
|
||||
@Nullable VibrationEffect vibrationEffect,
|
||||
@NonNull Ringtone.Injectables injectables,
|
||||
@Nullable VolumeShaper.Configuration volumeShaperConfig,
|
||||
@@ -100,7 +95,7 @@ public class LocalRingtonePlayer
|
||||
mediaPlayer.setAudioAttributes(audioAttributes);
|
||||
mediaPlayer.setPreferredDevice(preferredDevice);
|
||||
mediaPlayer.setLooping(initialLooping);
|
||||
mediaPlayer.setVolume(initialVolume);
|
||||
mediaPlayer.setVolume(isVibrationOnly ? 0 : initialVolume);
|
||||
if (initialHapticGeneratorEnabled) {
|
||||
hapticGenerator = injectables.createHapticGenerator(mediaPlayer);
|
||||
if (hapticGenerator != null) {
|
||||
@@ -121,8 +116,16 @@ public class LocalRingtonePlayer
|
||||
vibrationEffect = null;
|
||||
}
|
||||
}
|
||||
VibrationEffectPlayer vibrationEffectPlayer = (vibrationEffect == null) ? null :
|
||||
new VibrationEffectPlayer(
|
||||
vibrationEffect, audioAttributes, vibrator, initialLooping);
|
||||
if (isVibrationOnly && vibrationEffectPlayer != null) {
|
||||
// Abandon the media player now that it's confirmed to not have haptic channels.
|
||||
mediaPlayer.release();
|
||||
return vibrationEffectPlayer;
|
||||
}
|
||||
return new LocalRingtonePlayer(mediaPlayer, audioAttributes, injectables, audioManager,
|
||||
hapticGenerator, volumeShaper, vibrator, vibrationEffect);
|
||||
hapticGenerator, volumeShaper, vibrationEffectPlayer);
|
||||
} catch (SecurityException | IOException e) {
|
||||
if (hapticGenerator != null) {
|
||||
hapticGenerator.release();
|
||||
@@ -179,8 +182,11 @@ public class LocalRingtonePlayer
|
||||
vibrationEffect = null;
|
||||
}
|
||||
}
|
||||
VibrationEffectPlayer vibrationEffectPlayer = (vibrationEffect == null) ? null :
|
||||
new VibrationEffectPlayer(
|
||||
vibrationEffect, audioAttributes, vibrator, initialLooping);
|
||||
return new LocalRingtonePlayer(mediaPlayer, audioAttributes, injectables, audioManager,
|
||||
/* hapticGenerator= */ null, volumeShaper, vibrator, vibrationEffect);
|
||||
/* hapticGenerator= */ null, volumeShaper, vibrationEffectPlayer);
|
||||
} catch (SecurityException | IOException e) {
|
||||
Log.e(TAG, "Failed to open fallback ringtone");
|
||||
// TODO: vibration-effect-only / no-sound LocalRingtonePlayer.
|
||||
@@ -217,33 +223,8 @@ public class LocalRingtonePlayer
|
||||
}
|
||||
|
||||
private void maybeStartVibration() {
|
||||
if (mVibrationEffect != null && !mStartedVibration) {
|
||||
boolean isLooping = mMediaPlayer.isLooping();
|
||||
try {
|
||||
// Adjust the vibration effect to loop.
|
||||
VibrationEffect loopAdjustedEffect = mVibrationEffect.applyRepeatingIndefinitely(
|
||||
isLooping, VIBRATION_LOOP_DELAY_MS);
|
||||
mVibrator.vibrate(loopAdjustedEffect, mVibrationAttributes);
|
||||
mStartedVibration = true;
|
||||
} catch (Exception e) {
|
||||
// Catch exceptions widely, because we don't want to "leak" looping sounds or
|
||||
// vibrations if something goes wrong.
|
||||
Log.e(TAG, "Problem starting " + (isLooping ? "looping " : "") + "vibration "
|
||||
+ "for ringtone: " + mVibrationEffect, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void stopVibration() {
|
||||
if (mVibrationEffect != null && mStartedVibration) {
|
||||
try {
|
||||
mVibrator.cancel(mVibrationAttributes.getUsage());
|
||||
mStartedVibration = false;
|
||||
} catch (Exception e) {
|
||||
// Catch exceptions widely, because we don't want to "leak" looping sounds or
|
||||
// vibrations if something goes wrong.
|
||||
Log.e(TAG, "Problem stopping vibration for ringtone", e);
|
||||
}
|
||||
if (mVibrationPlayer != null) {
|
||||
mVibrationPlayer.play();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,7 +241,13 @@ public class LocalRingtonePlayer
|
||||
try {
|
||||
mMediaPlayer.stop();
|
||||
} finally {
|
||||
stopVibration(); // Won't throw: catches exceptions.
|
||||
if (mVibrationPlayer != null) {
|
||||
try {
|
||||
mVibrationPlayer.stopAndRelease();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Exception stopping ringtone vibration", e);
|
||||
}
|
||||
}
|
||||
if (mHapticGenerator != null) {
|
||||
mHapticGenerator.release();
|
||||
}
|
||||
@@ -282,25 +269,19 @@ public class LocalRingtonePlayer
|
||||
return;
|
||||
}
|
||||
mMediaPlayer.setLooping(looping);
|
||||
// If transitioning from looping to not-looping during play, then cancel the vibration.
|
||||
if (mVibrationEffect != null && mMediaPlayer.isPlaying()) {
|
||||
if (wasLooping) {
|
||||
stopVibration();
|
||||
} else {
|
||||
// Won't restart the vibration to be looping if it was already started.
|
||||
maybeStartVibration();
|
||||
}
|
||||
if (mVibrationPlayer != null) {
|
||||
mVibrationPlayer.setLooping(looping);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHapticGeneratorEnabled(boolean enabled) {
|
||||
if (mVibrationEffect != null) {
|
||||
// Ignore haptic generator changes if a vibration effect is present. The decision to
|
||||
if (mVibrationPlayer != null) {
|
||||
// Ignore haptic generator changes if a vibration player is present. The decision to
|
||||
// use one or the other happens before this object is constructed.
|
||||
return;
|
||||
}
|
||||
if (enabled && mHapticGenerator == null) {
|
||||
if (enabled && mHapticGenerator == null && !hasHapticChannels()) {
|
||||
mHapticGenerator = mInjectables.createHapticGenerator(mMediaPlayer);
|
||||
}
|
||||
if (mHapticGenerator != null) {
|
||||
@@ -311,6 +292,7 @@ public class LocalRingtonePlayer
|
||||
@Override
|
||||
public void setVolume(float volume) {
|
||||
mMediaPlayer.setVolume(volume);
|
||||
// no effect on vibration player
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -324,5 +306,103 @@ public class LocalRingtonePlayer
|
||||
sActiveMediaPlayers.remove(this);
|
||||
}
|
||||
mp.setOnCompletionListener(null); // Help the Java GC: break the refcount cycle.
|
||||
// No effect on vibration: either it's looping and this callback only happens when stopped,
|
||||
// or it's not looping, in which case the vibration should play to its own completion.
|
||||
}
|
||||
|
||||
/** A RingtonePlayer that only plays a VibrationEffect. */
|
||||
static class VibrationEffectPlayer implements Ringtone.RingtonePlayer {
|
||||
private static final int VIBRATION_LOOP_DELAY_MS = 200;
|
||||
private final VibrationEffect mVibrationEffect;
|
||||
private final VibrationAttributes mVibrationAttributes;
|
||||
private final Vibrator mVibrator;
|
||||
private boolean mIsLooping;
|
||||
private boolean mStartedVibration;
|
||||
|
||||
VibrationEffectPlayer(@NonNull VibrationEffect vibrationEffect,
|
||||
@NonNull AudioAttributes audioAttributes,
|
||||
@NonNull Vibrator vibrator, boolean initialLooping) {
|
||||
mVibrationEffect = vibrationEffect;
|
||||
mVibrationAttributes = new VibrationAttributes.Builder(audioAttributes).build();
|
||||
mVibrator = vibrator;
|
||||
mIsLooping = initialLooping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean play() {
|
||||
if (!mStartedVibration) {
|
||||
try {
|
||||
// Adjust the vibration effect to loop.
|
||||
VibrationEffect loopAdjustedEffect =
|
||||
mVibrationEffect.applyRepeatingIndefinitely(
|
||||
mIsLooping, VIBRATION_LOOP_DELAY_MS);
|
||||
mVibrator.vibrate(loopAdjustedEffect, mVibrationAttributes);
|
||||
mStartedVibration = true;
|
||||
} catch (Exception e) {
|
||||
// Catch exceptions widely, because we don't want to "leak" looping sounds or
|
||||
// vibrations if something goes wrong.
|
||||
Log.e(TAG, "Problem starting " + (mIsLooping ? "looping " : "") + "vibration "
|
||||
+ "for ringtone: " + mVibrationEffect, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlaying() {
|
||||
return mStartedVibration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopAndRelease() {
|
||||
if (mStartedVibration) {
|
||||
try {
|
||||
mVibrator.cancel(mVibrationAttributes.getUsage());
|
||||
mStartedVibration = false;
|
||||
} catch (Exception e) {
|
||||
// Catch exceptions widely, because we don't want to "leak" looping sounds or
|
||||
// vibrations if something goes wrong.
|
||||
Log.e(TAG, "Problem stopping vibration for ringtone", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPreferredDevice(AudioDeviceInfo audioDeviceInfo) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLooping(boolean looping) {
|
||||
if (looping == mIsLooping) {
|
||||
return;
|
||||
}
|
||||
mIsLooping = looping;
|
||||
if (mStartedVibration) {
|
||||
if (!mIsLooping) {
|
||||
// Was looping, stop looping
|
||||
stopAndRelease();
|
||||
}
|
||||
// Else was not looping, but can't interfere with a running vibration without
|
||||
// restarting it, and don't know if it was finished. So do nothing: apps shouldn't
|
||||
// toggle looping after calling play anyway.
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHapticGeneratorEnabled(boolean enabled) {
|
||||
// n/a
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVolume(float volume) {
|
||||
// n/a
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasHapticChannels() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,14 +286,29 @@ public class Ringtone {
|
||||
stopAndReleaseActivePlayer();
|
||||
}
|
||||
|
||||
boolean vibrationOnly = (mEnabledMedia & MEDIA_ALL) == MEDIA_VIBRATION;
|
||||
// Vibration can come from the audio file if using haptic generator or if haptic
|
||||
// channels are a possibility.
|
||||
boolean maybeAudioVibration = mUri != null && mInjectables.isHapticPlaybackSupported()
|
||||
&& (mHapticGeneratorEnabled || !mAudioAttributes.areHapticChannelsMuted());
|
||||
|
||||
// VibrationEffect only, use the simplified player without checking for haptic channels.
|
||||
if (vibrationOnly && !maybeAudioVibration && mVibrationEffect != null) {
|
||||
mActivePlayer = new LocalRingtonePlayer.VibrationEffectPlayer(
|
||||
mVibrationEffect, mAudioAttributes, mVibrator, mIsLooping);
|
||||
return true;
|
||||
}
|
||||
|
||||
AudioDeviceInfo preferredDevice =
|
||||
mPreferBuiltinDevice ? getBuiltinDevice(mAudioManager) : null;
|
||||
if (mUri != null) {
|
||||
mActivePlayer = LocalRingtonePlayer.create(mContext, mAudioManager, mVibrator, mUri,
|
||||
mAudioAttributes, mVibrationEffect, mInjectables, mVolumeShaperConfig,
|
||||
preferredDevice, mHapticGeneratorEnabled, mIsLooping, mVolume);
|
||||
mAudioAttributes, vibrationOnly, mVibrationEffect, mInjectables,
|
||||
mVolumeShaperConfig, preferredDevice, mHapticGeneratorEnabled, mIsLooping,
|
||||
mVolume);
|
||||
} else {
|
||||
// Using the remote player won't help play a null Uri. Revert straight to fallback.
|
||||
// The vibration-only case was already covered above.
|
||||
mActivePlayer = createFallbackRingtonePlayer();
|
||||
// Fall through to attempting remote fallback play if null.
|
||||
}
|
||||
@@ -388,6 +403,10 @@ public class Ringtone {
|
||||
* corresponds to no attenuation being applied.
|
||||
*/
|
||||
public void setVolume(float volume) {
|
||||
// Ignore if sound not enabled.
|
||||
if ((mEnabledMedia & MEDIA_SOUND) == 0) {
|
||||
return;
|
||||
}
|
||||
if (volume < 0.0f) {
|
||||
volume = 0.0f;
|
||||
} else if (volume > 1.0f) {
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.mediaframeworktest.unit;
|
||||
|
||||
import static android.media.Ringtone.MEDIA_SOUND;
|
||||
import static android.media.Ringtone.MEDIA_SOUND_AND_VIBRATION;
|
||||
import static android.media.Ringtone.MEDIA_VIBRATION;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
@@ -31,7 +32,6 @@ import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
@@ -293,7 +293,6 @@ public class RingtoneTest {
|
||||
ringtone.play();
|
||||
|
||||
verifyLocalPlay(mockMediaPlayer);
|
||||
verify(mockMediaPlayer).isLooping(); // When starting the vibration.
|
||||
verify(mMockVibrator).vibrate(VIBRATION_EFFECT, RINGTONE_VIB_ATTRIBUTES);
|
||||
|
||||
// Verify dynamic controls.
|
||||
@@ -303,8 +302,7 @@ public class RingtoneTest {
|
||||
// Set looping doesn't affect an already-started vibration.
|
||||
when(mockMediaPlayer.isLooping()).thenReturn(false); // Checks original
|
||||
ringtone.setLooping(true);
|
||||
verify(mockMediaPlayer).isPlaying(); // Vibration check.
|
||||
verify(mockMediaPlayer, times(2)).isLooping(); // Current state, second isLooping call.
|
||||
verify(mockMediaPlayer).isLooping();
|
||||
verify(mockMediaPlayer).setLooping(true);
|
||||
|
||||
// This is ignored because there's a vibration effect being used.
|
||||
@@ -323,6 +321,160 @@ public class RingtoneTest {
|
||||
verifyNoMoreInteractions(mMockVibrator);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRingtone_localMediaWithVibrationOnly() throws Exception {
|
||||
when(mMockVibrator.hasVibrator()).thenReturn(true);
|
||||
Ringtone ringtone =
|
||||
newBuilder(MEDIA_VIBRATION, RINGTONE_ATTRIBUTES)
|
||||
// TODO: set sound uri too in diff test
|
||||
.setVibrationEffect(VIBRATION_EFFECT)
|
||||
.build();
|
||||
assertThat(ringtone).isNotNull();
|
||||
assertThat(ringtone.isUsingRemotePlayer()).isFalse();
|
||||
verify(mMockVibrator).hasVibrator();
|
||||
|
||||
// Verify all the properties.
|
||||
assertThat(ringtone.getEnabledMedia()).isEqualTo(MEDIA_VIBRATION);
|
||||
assertThat(ringtone.getUri()).isNull();
|
||||
assertThat(ringtone.getVibrationEffect()).isEqualTo(VIBRATION_EFFECT);
|
||||
|
||||
// Play
|
||||
ringtone.play();
|
||||
|
||||
verify(mMockVibrator).vibrate(VIBRATION_EFFECT, RINGTONE_VIB_ATTRIBUTES);
|
||||
|
||||
// Verify dynamic controls (no-op without sound)
|
||||
ringtone.setVolume(0.8f);
|
||||
|
||||
// Set looping doesn't affect an already-started vibration.
|
||||
ringtone.setLooping(true);
|
||||
|
||||
// This is ignored because there's a vibration effect being used and no sound.
|
||||
ringtone.setHapticGeneratorEnabled(true);
|
||||
|
||||
// Release
|
||||
ringtone.stop();
|
||||
verify(mMockVibrator).cancel(VibrationAttributes.USAGE_RINGTONE);
|
||||
|
||||
// This test is intended to strictly verify all interactions with MediaPlayer in a local
|
||||
// playback case. This shouldn't be necessary in other tests that have the same basic
|
||||
// setup.
|
||||
verifyZeroInteractions(mMockRemotePlayer);
|
||||
verifyNoMoreInteractions(mMockVibrator);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRingtone_localMediaWithVibrationOnlyAndSoundUriNoHapticChannels()
|
||||
throws Exception {
|
||||
// A media player will still be created for vibration-only because the vibration can come
|
||||
// from haptic channels on the sound file (although in this case it doesn't).
|
||||
MediaPlayer mockMediaPlayer = mMediaPlayerRule.expectLocalMediaPlayer();
|
||||
mMediaPlayerRule.setHasHapticChannels(mockMediaPlayer, false);
|
||||
when(mMockVibrator.hasVibrator()).thenReturn(true);
|
||||
Ringtone ringtone =
|
||||
newBuilder(MEDIA_VIBRATION, RINGTONE_ATTRIBUTES)
|
||||
.setUri(SOUND_URI)
|
||||
.setVibrationEffect(VIBRATION_EFFECT)
|
||||
.build();
|
||||
assertThat(ringtone).isNotNull();
|
||||
assertThat(ringtone.isUsingRemotePlayer()).isFalse();
|
||||
verify(mMockVibrator).hasVibrator();
|
||||
|
||||
// Verify all the properties.
|
||||
assertThat(ringtone.getEnabledMedia()).isEqualTo(MEDIA_VIBRATION);
|
||||
assertThat(ringtone.getUri()).isEqualTo(SOUND_URI);
|
||||
assertThat(ringtone.getVibrationEffect()).isEqualTo(VIBRATION_EFFECT);
|
||||
|
||||
// Prepare
|
||||
// Uses attributes with haptic channels enabled, but will abandon the MediaPlayer when it
|
||||
// knows there aren't any.
|
||||
verifyLocalPlayerSetup(mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
|
||||
verify(mockMediaPlayer).setVolume(0.0f); // Vibration-only: sound muted.
|
||||
verify(mockMediaPlayer).setLooping(false);
|
||||
verify(mockMediaPlayer).prepare();
|
||||
verify(mockMediaPlayer).release(); // abandoned: no haptic channels.
|
||||
|
||||
// Play
|
||||
ringtone.play();
|
||||
|
||||
verify(mMockVibrator).vibrate(VIBRATION_EFFECT, RINGTONE_VIB_ATTRIBUTES);
|
||||
|
||||
// Verify dynamic controls (no-op without sound)
|
||||
ringtone.setVolume(0.8f);
|
||||
|
||||
// Set looping doesn't affect an already-started vibration.
|
||||
ringtone.setLooping(true);
|
||||
|
||||
// This is ignored because there's a vibration effect being used and no sound.
|
||||
ringtone.setHapticGeneratorEnabled(true);
|
||||
|
||||
// Release
|
||||
ringtone.stop();
|
||||
verify(mMockVibrator).cancel(VibrationAttributes.USAGE_RINGTONE);
|
||||
|
||||
// This test is intended to strictly verify all interactions with MediaPlayer in a local
|
||||
// playback case. This shouldn't be necessary in other tests that have the same basic
|
||||
// setup.
|
||||
verifyZeroInteractions(mMockRemotePlayer);
|
||||
verifyNoMoreInteractions(mMockVibrator);
|
||||
verifyNoMoreInteractions(mockMediaPlayer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRingtone_localMediaWithVibrationOnlyAndSoundUriWithHapticChannels()
|
||||
throws Exception {
|
||||
MediaPlayer mockMediaPlayer = mMediaPlayerRule.expectLocalMediaPlayer();
|
||||
when(mMockVibrator.hasVibrator()).thenReturn(true);
|
||||
mMediaPlayerRule.setHasHapticChannels(mockMediaPlayer, true);
|
||||
Ringtone ringtone =
|
||||
newBuilder(MEDIA_VIBRATION, RINGTONE_ATTRIBUTES)
|
||||
.setUri(SOUND_URI)
|
||||
.setVibrationEffect(VIBRATION_EFFECT)
|
||||
.build();
|
||||
assertThat(ringtone).isNotNull();
|
||||
assertThat(ringtone.isUsingRemotePlayer()).isFalse();
|
||||
verify(mMockVibrator).hasVibrator();
|
||||
|
||||
// Verify all the properties.
|
||||
assertThat(ringtone.getEnabledMedia()).isEqualTo(MEDIA_VIBRATION);
|
||||
assertThat(ringtone.getUri()).isEqualTo(SOUND_URI);
|
||||
assertThat(ringtone.getVibrationEffect()).isEqualTo(VIBRATION_EFFECT);
|
||||
|
||||
// Prepare
|
||||
// Uses attributes with haptic channels enabled, but will use the effect when there aren't
|
||||
// any present.
|
||||
verifyLocalPlayerSetup(mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
|
||||
verify(mockMediaPlayer).setVolume(0.0f); // Vibration-only: sound muted.
|
||||
verify(mockMediaPlayer).setLooping(false);
|
||||
verify(mockMediaPlayer).prepare();
|
||||
|
||||
// Play
|
||||
ringtone.play();
|
||||
// Vibrator.vibrate isn't called because the vibration comes from the sound.
|
||||
verifyLocalPlay(mockMediaPlayer);
|
||||
|
||||
// Verify dynamic controls (no-op without sound)
|
||||
ringtone.setVolume(0.8f);
|
||||
|
||||
when(mockMediaPlayer.isLooping()).thenReturn(false); // Checks original
|
||||
ringtone.setLooping(true);
|
||||
verify(mockMediaPlayer).isLooping();
|
||||
verify(mockMediaPlayer).setLooping(true);
|
||||
|
||||
// This is ignored because it's using haptic channels.
|
||||
ringtone.setHapticGeneratorEnabled(true);
|
||||
|
||||
// Release
|
||||
ringtone.stop();
|
||||
verifyLocalStop(mockMediaPlayer);
|
||||
|
||||
// This test is intended to strictly verify all interactions with MediaPlayer in a local
|
||||
// playback case. This shouldn't be necessary in other tests that have the same basic
|
||||
// setup.
|
||||
verifyZeroInteractions(mMockRemotePlayer);
|
||||
verifyZeroInteractions(mMockVibrator);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRingtone_localMediaWithVibrationPrefersHapticChannels() throws Exception {
|
||||
MediaPlayer mockMediaPlayer = mMediaPlayerRule.expectLocalMediaPlayer();
|
||||
|
||||
Reference in New Issue
Block a user