The standard way of handling a loss of AudioFocus that can be "ducked" is as follows:
OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {
public void onAudioFocusChange(int focusChange) {
if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
// Lower the volume
}
}
};
However, if this is implemented as shown you will lower the volume even if the notification is inaudible, such as when DnD mode is turned on in Android 5.0+. There are apparently several different methods for determining if DnD is active: getAutomaticZenRule() (wtf?), getCurrentInterruptionFilter(), and getNotificationPolicy() (which requires special access). None of which are available in < API 23.
Is there a succinct way of telling if the stream requesting AudioFocus that is duck-able is actually audible without muddling through all of the above? It seems Google Play Music handles this case fine.
OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {
public void onAudioFocusChange(int focusChange) {
if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
if (!(audioManager.getStreamVolume(AudioManager.STREAM_NOTIFICATION) == 0))
// Lower the volume
}
}
};
does the job.
Related
I have seen many Android Player online that as soon it start playing other app loses the focus and stop playing.
At other hand, as soon the gained focused app stop playing, focus Loosed app start playing again.
can any one suggest what am i missing here to achieve the same in my app? I want as soon other app stop playing my app should GAIN focus and start playing..
private void setupAudioManager() {
audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
audioManager.requestAudioFocus(audioFocusListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
audioFocusListener = new OnAudioFocusChangeListener() {
#Override
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AUDIOFOCUS_GAIN:
if (mediaPlayer == null) setupMediaPlayer();
else if (!mediaPlayer.isPlaying()) {
play();
}
mediaPlayer.setVolume(MEDIA_PLAYER_LEFT_VOLUME, MEDIA_PLAYER_RIGHT_VOLUME);
break;
case AUDIOFOCUS_LOSS:
if (isPlaying()) {
Intent intent = new Intent("HomeActivity");
intent.putExtra("playerState", "pause");
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
}
break;
case AUDIOFOCUS_LOSS_TRANSIENT:
if (isPlaying()) pause();
break;
case AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
if (isPlaying()) mediaPlayer.setVolume(MEDIA_PLAYER_LEFT_VOLUME_LOW,
MEDIA_PLAYER_RIGHT_VOLUME_LOW);
break;
}
}
};
}
Looking for you suggestion.
Here are my findings
In case of Permanent loss of focus
If the audio focus loss is permanent (AUDIOFOCUS_LOSS), another application is playing audio. Your app should pause play immediately. At this point your app will never receive an AUDIOFOCUS_GAIN callback. To restart playback the user must take an explicit action, like pressing the play transport control in a notification or app UI.
After pausing your app should wait a short interval and then stop its media session to release resources and abandon audio focus. Delaying the stop call gives the user the opportunity to restart your app's playback. This can be useful if your app goes silent because the user accidentally started a different app that requested the audio focus.
The following code snippet demonstrates how to implement the
OnAudioFocusChangeListener and its onAudioFocusChange() callback.
Notice the use of a Handler to delay the stop callback on a permanent
loss of audio focus.
private Handler mHandler = new Handler();
AudioManager.OnAudioFocusChangeListener afChangeListener =
new AudioManager.OnAudioFocusChangeListener() {
public void onAudioFocusChange(int focusChange) {
if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
// Permanent loss of audio focus
// Pause playback immediately
mediaController.getTransportControls().pause();
// Wait 30 seconds before stopping playback
mHandler.postDelayed(mDelayedStopRunnable,
TimeUnit.SECONDS.toMillis(30));
}
else if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT) {
// Pause playback
} else if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
// Lower the volume, keep playing
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
// Your app has been granted audio focus again
// Raise volume to normal, restart playback if necessary
}
}
};
The handler uses a Runnable that looks like this:
private Runnable mDelayedStopRunnable = new Runnable() {
#Override
public void run() {
mediaController.getTransportControls().stop();
}
};
To ensure the delayed stop does not kick in if the user restarts
playback, call mHandler.removeCallbacks(mDelayedStopRunnable) in
response to any state changes. For example, call removeCallbacks() in
your Callback's onPlay(), onSkipToNext(), etc. You should also call
this method in your service's onDestroy() callback when cleaning up
the resources used by your service.
How to pause the audio player when I launch the radio app that I develop?
And "vice-versa" , How to pause my app when the user launch the audioplayer when my app is running in background.
You App needs to request the Audio-Focus, more about that here. Once you no longer need it you can abandon the focus, which returns it to the Application which had it previously to yours.
Use OnAudioFocusChangeListener to detect if there are other apps requesting audio focus; if so, pause your audio player.
am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
afChangeListener = new OnAudioFocusChangeListener() {
public void onAudioFocusChange(int focusChange) {
if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT){
// Pause playback
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
// Resume playback
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
abandonAudioFocus();
}
}
};
Similar SO question is here
I am writing an app with TTS, I am able to use UtteranceProgressListener (don't worry about older version on this topic) to gain Audio focus from Music players, and give focus back to Music players after the speech is done. But the app TTS still overlaps with Google Maps (navigator) voices.
Is there anyway I can tell when Map is speaking, and my voice can be queued, or even flushed (because right now, I cannot discern/understand either of them when both of them are talking).
Or someone can point me to the source of Google Map (or hidden APIs that I can use), I understand that Google map is not part of the Android open source.
Below is snippet of my code:
#TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
private class utteranceListener extends UtteranceProgressListener {
private OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {
public void onAudioFocusChange(int focusChange) {
if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
// Pause playback
audioManager.abandonAudioFocus(afChangeListener);
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK){
// we don't duck, just abandon focus
audioManager.abandonAudioFocus(afChangeListener);
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
// Resume playback
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
audioManager.abandonAudioFocus(afChangeListener);
}
}
};
#Override
public void onDone(String utteranceId)
{
audioManager.abandonAudioFocus(afChangeListener);
}
#Override
public synchronized void onError(String utteranceId)
{
audioManager.abandonAudioFocus(afChangeListener);
}
#Override
public void onStart(String utteranceId)
{
int result = audioManager.requestAudioFocus(afChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
}
}
By the way, on music and TTS, I like old-fashion AUDIOFOCUS_LOSS_TRANSIENT better than AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK, a 'ducked' music still makes (TTS) speech hard to listen, especially when playing Rap music, which sometimes is just 'a speech/talk'.
I've got it mostly resolved, with one exception, when Google Map is talking, my code (below) still returns AUDIOFOCUS_REQUEST_GRANTED, so I still have overlapping here.
audioManager.requestAudioFocus(afChangeListener, AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
On the things that work, two different concepts, UtterancePrograssListener is for events triggered by your own app, like whenever your speech is done or your speech is about to start; and OnAudioFocusChangeListener is for events triggered by other apps, like google maps grabbed audio focus from you.
So in case that I want to have my own app stop talking when Google Map is about to announce driving directions, below code would stop my talking:
public void onAudioFocusChange(int focusChange) {
if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
// Someone else has taken over the audio focus,
// abandonAudioFocus() would not do anything.
// you can only stop(), or lower your voice.
tts.stop();
audioManager.abandonAudioFocus(afChangeListener);
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK){
// this is what Google Map actually triggers
tts.stop();
audioManager.abandonAudioFocus(afChangeListener);
}
.....
When Android receives an SMS the notification sends an AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK focus Change. I handle this by cutting the volume of the media player by 50%.
E.g.
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
// E.g. SMS
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK :
playerDuck(true);
break;
case AudioManager.AUDIOFOCUS_GAIN :
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT :
playerDuck(false);
playerResume();
break;
}
}
public synchronized void playerDuck(boolean duck) {
if (mediaPlayer != null) {
// Reduce the volume by half when ducking - otherwise play at full volume.
mediaPlayer.setVolume(duck ? 0.5f : 1.0f, duck ? 0.5f : 1.0f);
}
}
This works fine when listening to the mediaplayer through the device's speaker but if you plug the headphones in then, instead of reducing the volume temporarily, the mediaplayer is muted whilst the notification plays.
How should I be handling the change in audio focus differently when the headphones are plugged in?
Im using this code to get AudioFocus and it works ok with
Android Music app ( the one preinstalled )
int result = audioManager.requestAudioFocus(meService, AudioManager.STREAM_MUSIC,AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
However when i release it with this code
audioManager.abandonAudioFocus(meService);
The Android Music app ( the one preinstalled ) does not continue playing.
if i use the AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK it works
but Android Music app is not lowering the volume enough.
Any ides why Android Music app is not resuming playback?
im using api8 and using the onAudioFocusChange
You should release it in MediaPlay.onCompleteListener(){}
After trying 4 players non of them are ducking. I might be doing something wrong and would like to see that. I answer my own question that we have to live with this.
This way works fine for me.
public class HomeActivity extends Activity implements AudioManager.OnAudioFocusChangeListener {
// Other stuff
#Override
public void onAudioFocusChange(int focusChange) {
if (focusChange == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
Log.i("HomeActivity", "Audio focus granted.");
}else if (focusChange == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
Log.i("HomeActivity", "Audio focus failed.");
}
}
}
Request AudioFocus:
private void requestAudioFocus(){
AudioManager am = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
// Request audio focus for playback
am.requestAudioFocus(this,
// Use the music stream.
AudioManager.STREAM_MUSIC,
// Request permanent focus.
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
}
Abandon AudioFocus:
private void abandonAudioFocus(){
AudioManager am = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
am.abandonAudioFocus(this);
}
Hope this would help you.