I thought that I could turn on speakerphone and set the volume to maximum within PhoneStateListener
, but I can't get it to work when I test it. I am using AudioManager
to try to turn on the speakerphone and set the volume. Is this not the correct way to do it? Why does the following code fail to enable speakerphone?
class PlaceCall : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
...
val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
telephonyManager.listen(CallListener(this), PhoneStateListener.LISTEN_CALL_STATE)
val callIntent = Intent(Intent.ACTION_CALL, Uri.parse("tel:" + phoneNumber))
startActivity(callIntent)
...
}
}
class CallListener(cont: Context) : PhoneStateListener() {
private var context: Context = cont
override fun onCallStateChanged(state: Int, phoneNumber: String?) {
val audioManager: AudioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, audioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL), 0) //I believe this should set call volume to max?
audioManager.isSpeakerphoneOn = true //I believe this should enable speakerphone, but it doesn't during my tests
}
}
AndroidManifest.xml
...
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
...
Edit:
I tried adding a BroadcastReceiver
as well but it didn't change the outcome. Speakerphone is still not enabled. This doesn't make sense and I don't know what I am doing wrong. Below is updated code with the new BroadcastReciever added (above code is still the same).
AndroidManifest.xml (updated)
...
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<application>
<receiver android:name=".ServiceReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE" />
</intent-filter>
</receiver>
</application>
...
class ServiceReceiver : BroadcastReceiver() {
lateinit var telephonyManager: TelephonyManager
override fun onReceive(context: Context, intent: Intent) {
telephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
telephonyManager.listen(CallListener(context), PhoneStateListener.LISTEN_CALL_STATE)
val audioManager: AudioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
audioManager.mode = AudioManager.MODE_IN_CALL
audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, audioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL), 0)
audioManager.isSpeakerphoneOn = true
}
}
a PhoneStateListener
as its name suggests is just a listener, it can't perform actions on the current call, that's the responsibility of the default Phone
app.
The good news is that you can have the user set your app as the default Phone
app, but it forces you to implement certain activities and services which might be out of scope for your app.
You'll need to implement your own dialpad activity for making calls, your own InCallService
for handling active calls, and your own in-call UI for allowing the user to mute, put on speaker, connect to bluetooth, put a call on call-waiting, send dialtones while in a call, etc.
You can check out this series of medium posts on how to make your app the default Phone app.
Once you've made your app the default phone app, and your app controls the current app, you can use setAudioRoute
to turn on speaker phone.
EDIT:
You can try using setSpeakerphoneOn
in AudioManager (isX
is always used to check a value, setX
is used to modify a value), however the docs say:
This method should only be used by applications that replace the platform-wide management of audio settings or the main telephony application.
I'm not sure if Android enforces that by preventing apps from calling this method, and if so, on which Android platform, but you can try it out.
There is also a way to use java reflection to access hidden API, see here, but using reflection will almost definitely break on recent version of Android, and might block your app from getting into Google Play. Even still, might worth playing around with.