Yes, It's possible. I successfully do it a few days ago. There are 2 solutions for it:
Solution 1:
Suppose you are in a normal mood & Headset connected. We just need to call setPreferredDevice function to set your preferred audio input/output device. Our input source is already coming from the headset. So we only need to route the audio output source to the Phone Speaker. Here is my sample code by Media Player:
@RequiresApi(Build.VERSION_CODES.P)
private fun playAudioFromPhoneSpeaker(context: Context, audioResId: Int) {
val mMediaPlayer: MediaPlayer = MediaPlayer.create(context, audioResId)
val mCurrentDevice: AudioDeviceInfo? = mMediaPlayer.preferredDevice
val mDeviceSpeaker: AudioDeviceInfo? = getDeviceOutputSpeaker(context)
mDeviceSpeaker?.let { speaker ->
if (mCurrentDevice != speaker) {
val result: Boolean? = mMediaPlayer.setPreferredDevice(mDeviceSpeaker)
Log.i("PreferredDevice", "is set as speaker: $result")
}
}
mMediaPlayer.start()
mMediaPlayer.setOnCompletionListener { mediaPlayer ->
mediaPlayer.release()
}
}
@RequiresApi(Build.VERSION_CODES.M)
private fun getDeviceOutputSpeaker(context: Context): AudioDeviceInfo? {
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val mAudioDeviceOutputList = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)
mAudioDeviceOutputList?.filterNotNull()?.forEach { speaker ->
if (speaker.type == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
return speaker
}
}
return null
}
The positive thing about this solution is that we don't need to think about the input source. We only change the audio output source and it doesn't have any impact on the input source.
The problem is setPreferredDevice of MediaPlayer come from Android 9. So this solution is only supported from Android 9.
Solution 2:
In 2nd solution we turn on device speaker by Audio Manager. So after turn on the device speaker the audio input & output source will come to the device internal speaker & device mic. Now we have to route the mic to the headset. Sample code:
private val mAudioRecord: AudioRecord? = null
private fun RecordAudioFromHeadphone(context: Context) {
//Step 1: Turn on Speaker
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
audioManager.isSpeakerphoneOn = true
//Step 2: Init AudioRecorder TODO for you
//Step 3: Route mic to headset
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
setPreferredInputMic(getWiredHeadPhoneMic(this))
}
}
@RequiresApi(Build.VERSION_CODES.M)
private fun setPreferredInputMic(mAudioDeviceInfo: AudioDeviceInfo?) {
val result: Boolean? = mAudioRecord?.setPreferredDevice(mAudioDeviceInfo)
}
@RequiresApi(Build.VERSION_CODES.M)
private fun getWiredHeadPhoneMic(context: Context): AudioDeviceInfo? {
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val mAudioDeviceOutputList = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)
mAudioDeviceOutputList?.filterNotNull()?.forEach { device ->
if (device.type == AudioDeviceInfo.TYPE_WIRED_HEADSET) {
return device
}
}
return null
}
The positive think of this solution is supported from Android 6. And negative thing is it's little bit complex and you have to maintain the voice input source.
AudioRecorder, MediaRecorder, AudioTrack also support this kind of solution.
Thanks