66

I have implemented an activity that plays media from a URL in Android.

In order to add pause functionality when a call is incoming I created a receiver that sets a variable when the call is coming. The activity reads this variable in onPause(), pauses the music and resets it. When the call is done and the activity is resumed, the music is resumed in onResume().

This works fine as long the activity has the focus. If I go back to home screen while the music is playing, and then the call comes, onPause() is not called. Hence, I can't stop and start the music.

Has anybody implemented a media player that intercepts incoming/outgoing calls and stops/starts music correctly?

Matt Ke
  • 3,599
  • 12
  • 30
  • 49
user669231
  • 1,371
  • 3
  • 18
  • 27

6 Answers6

96

There are a few things you can do:

First of all, you can listen for changes in the call state using a PhoneStateListener. You can register the listener in the TelephonyManager:

PhoneStateListener phoneStateListener = new PhoneStateListener() {
    @Override
    public void onCallStateChanged(int state, String incomingNumber) {
        if (state == TelephonyManager.CALL_STATE_RINGING) {
            //Incoming call: Pause music
        } else if(state == TelephonyManager.CALL_STATE_IDLE) {
            //Not in call: Play music
        } else if(state == TelephonyManager.CALL_STATE_OFFHOOK) {
            //A call is dialing, active or on hold
        }
        super.onCallStateChanged(state, incomingNumber);
    }
};
TelephonyManager mgr = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
if(mgr != null) {
    mgr.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
}

Remember to unregister the listener when it's no longer needed using the PhoneStateListener.LISTEN_NONE:

TelephonyManager mgr = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
if(mgr != null) {
    mgr.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
}

For more information read the documentation.

Another thing you can do is listening for the broadcast android.intent.action.PHONE_STATE. It will contain the extra TelephonyManager.EXTRA_STATE which will give you information about the call. Take a look at the documentation here.

Please note that you'll need the android.permission.READ_PHONE_STATE-permission in both cases.

Kaloer
  • 3,773
  • 2
  • 27
  • 33
  • Do I need to register the PhoneStateListener in my Activity that plays music? What if that activity has lost focus? Will the listener still work? – user669231 Apr 10 '11 at 12:20
  • 2
    Yes the listener needs to be registered in the Activity which plays music, and it will be called if the activity has lost focus (as long as it has not been killed). However, it may be more appropriate to use a Service to play the music instead of an Activity as the Activity may be killed. – Kaloer Apr 11 '11 at 07:14
  • 2
    This worked for me. I created a service & registered the phonestatelistener in it. Now even if my activity loses focus(like user goes to the home screen by pressing the home button), the incoming calls get detected & I am able to stop & start music – user669231 Apr 12 '11 at 18:40
  • 3
    You should also request AudioManager::requestAudioFocus(), so when the focus has been granted you can play the stream; AudioManager.OnAudioFocusChangeListener::onAudioFocusChange(int ) depending on the change application should pause/play, refer to AudioManager and OnAudioFocusChangeListener documentation. – Vamsi Jan 09 '12 at 11:14
  • @kaloer we need the use the phonestatelistener where we are using audio player activity or it has to be in sepereate one – Karthick M Sep 13 '13 at 13:05
  • I get an error here, `if(mgr != null) {` It says, `Unknown class: mgr` – Ruchir Baronia Nov 11 '15 at 01:05
58

I think that AudioManager is the best and fast solution. Here there is my implementation example:

public class MyActivity extends Activity implements OnAudioFocusChangeListener {

private AudioManager mAudioManager;

@Override
public void onCreate(Bundle savedInstanceState) {
    mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
    mAudioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
}

@Override
 public void onDestroy(){
     super.onDestroy();
     ...
     mAudioManager.abandonAudioFocus(this);
     ...
 }

@Override
public void onAudioFocusChange(int focusChange) {
    if(focusChange<=0) {
        //LOSS -> PAUSE
    } else {
        //GAIN -> PLAY
       }
   }    
}

I hope it's helpful for you :-)

Sham Dhiman
  • 1,348
  • 1
  • 21
  • 59
mickesource
  • 591
  • 5
  • 3
  • 6
    Those who want to control not only GSM incoming but also VOIP Call such as Skype, Facebook messager then follow the way as @mickesource explained. – Frank Myat Thu Jul 09 '16 at 17:00
  • 3
    I have implemented this in my music service like "public class ServiceMusicPlayer extends Service implements AudioManager.OnAudioFocusChangeListener" and works great Thanks! – Sandro Wiggers Sep 19 '16 at 00:58
  • 1
    Agree. Sooner or later you'll have to use this approach because PhoneStateListener doesn't recognize incoming calls from Whatsapp for example or other sounds like alarm clock. – ka3ak Apr 29 '18 at 14:52
  • I like this decision – dmytro May 08 '18 at 17:31
  • Good decision! Work's good. I created class NetPlayer that implements AudioManager.OnAudioFocusChangeListener and works great. Thanks! – Black_Zerg Aug 16 '18 at 18:16
  • This does not work if the user doesn't have a ringtone – Maxime Peloquin Apr 12 '19 at 21:55
  • I want to record the calls after detecting them now how can i do this? – suv Jul 04 '19 at 11:22
  • So if i use AudioManager OnAudioFocusChangeListener i don't need PhoneStateListener and TelephonyManager? – Vince VD Nov 07 '19 at 15:12
  • This no longer works . onAudioFocusChanged is no longer called with incoming phone calls on newer android sdk – Oluwasegun Wahaab Mar 03 '20 at 14:20
13

I think requestAudioFocus() should be able to handle this case automatically. You don't need to check call state explicitly.

Audio Focus is cooperative in nature. That is, applications are expected (and highly encouraged) to comply with the audio focus guidelines, but the rules are not enforced by the system. If an application wants to play loud music even after losing audio focus, nothing in the system will prevent that. However, the user is more likely to have a bad experience and will be more likely to uninstall the misbehaving application.

To request audio focus, you must call requestAudioFocus() from the AudioManager, as the example below demonstrates:

AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);

if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
    // could not get audio focus.
}
Shiv
  • 1,269
  • 11
  • 20
  • This will not handle incoming calls nicely. - They seem like they duck the audio automatically, but incoming calls do not trigger `onAudioFocusChange`. – Peter Ajtai May 08 '13 at 00:14
  • 2
    This is the recommended way according to docs: http://developer.android.com/intl/ru/guide/topics/media/mediaplayer.html – david72 Jan 12 '16 at 23:02
13

You can try a broadcast receiver.

Create a class CallReceiver which extends BroadcastReceiver.

package com.example.callreceiver;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;

public class CallReceiver extends BroadcastReceiver{
    TelephonyManager telManager;
    Context context;

@Override
public void onReceive(Context context, Intent intent) {


    this.context=context;

    telManager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
    telManager.listen(phoneListener, PhoneStateListener.LISTEN_CALL_STATE);

}

private final PhoneStateListener phoneListener = new PhoneStateListener() {
    @Override
    public void onCallStateChanged(int state, String incomingNumber) {
        try {
            switch (state) {
            case TelephonyManager.CALL_STATE_RINGING: {
                //PAUSE
            break;
            }
            case TelephonyManager.CALL_STATE_OFFHOOK: {

            break;
            }
            case TelephonyManager.CALL_STATE_IDLE: {
                //PLAY
            break;
            }
            default: { }
            }
            } catch (Exception ex) {

            }
        }
    };
 }

Then Add this line in manifest.xml file to register it on the App

<receiver android:name="CallReceiver" >
        <intent-filter>
            <action android:name="android.intent.action.PHONE_STATE" >
            </action>
        </intent-filter>
    </receiver>
Wrichik Basu
  • 1,005
  • 17
  • 28
kulakesh
  • 166
  • 1
  • 6
2

For me idle state was coming while there was incoming call, the quick fix is to check in the broadcast receiver

BroadcastReceiver phonestatereceiver = new BroadcastReceiver() {
  @Override
  public void onReceive(Context context, Intent intent) {
    Bundle extras = intent.getExtras();
    if (extras != null) {
      String state = extras.getString(TelephonyManager.EXTRA_STATE);
      if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
        //pause here
      }
      else if (state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {
        //pause here
      }
      else if (state.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
        //play here
      }
    }
  }
};
Inder Kumar Rathore
  • 39,458
  • 17
  • 135
  • 184
  • 1
    Thanks a lot, the receiver can be registered using: IntentFilter filter = new IntentFilter(); filter.addAction(android.telephony.TelephonyManager.ACTION_PHONE_STATE_CHANGED); registerReceiver(receiver,filter); – UpaJah Jun 16 '18 at 16:27
0

Based on mickesource Updated for android Oreo+ and kotlin . I added in service , you can follow same for activity or fragment .

class MusicService : Service(), AudioManager.OnAudioFocusChangeListener {

var audioManager: AudioManager? = null
var audioFocusRequest:AudioFocusRequest?=null


override fun onCreate() {
    super.onCreate()    
    setAudioFocusChangeListener()
}


private fun setAudioFocusChangeListener() {

    audioManager = getSystemService(AUDIO_SERVICE) as AudioManager?

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        audioFocusRequest=AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
            .setAudioAttributes(
                AudioAttributes.Builder()
                    .setUsage(AudioAttributes.USAGE_MEDIA)
                    .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                    .build()
            )
            .setAcceptsDelayedFocusGain(true)
            .setOnAudioFocusChangeListener(this).build()
        audioManager?.requestAudioFocus(audioFocusRequest!!)
    } else {
        @Suppress("deprecation")
        audioManager?.requestAudioFocus(
            this,
            AudioManager.STREAM_MUSIC,
            AudioManager.AUDIOFOCUS_GAIN
        )
    }
}

override fun onAudioFocusChange(focusChange: Int) {
    if(focusChange<=0){
        //Pause audio
    }else{
        //Play audio
    }
}

 

Tested on Android 10 for regular and what's app call

Manohar
  • 22,116
  • 9
  • 108
  • 144
  • This is the perfect answer as it's doesn't need any permissions, and this should be the case, Requeireng the phone permission for such feature is not a good thing. – Ahmed Ali Mar 23 '21 at 15:30