mirror of
https://github.com/nextcloud/talk-android
synced 2025-07-04 11:34:30 +01:00
Merge pull request #3535 from nextcloud/handle-audio-focus
Handle audio focus and becoming noisy
This commit is contained in:
commit
ffdb93204d
@ -31,10 +31,12 @@ package com.nextcloud.talk.chat
|
|||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.animation.ObjectAnimator
|
import android.animation.ObjectAnimator
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
import android.content.ClipboardManager
|
import android.content.ClipboardManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.content.res.AssetFileDescriptor
|
import android.content.res.AssetFileDescriptor
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
@ -42,7 +44,9 @@ import android.database.Cursor
|
|||||||
import android.graphics.drawable.BitmapDrawable
|
import android.graphics.drawable.BitmapDrawable
|
||||||
import android.graphics.drawable.ColorDrawable
|
import android.graphics.drawable.ColorDrawable
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.media.AudioFocusRequest
|
||||||
import android.media.AudioFormat
|
import android.media.AudioFormat
|
||||||
|
import android.media.AudioManager
|
||||||
import android.media.AudioRecord
|
import android.media.AudioRecord
|
||||||
import android.media.MediaPlayer
|
import android.media.MediaPlayer
|
||||||
import android.media.MediaRecorder
|
import android.media.MediaRecorder
|
||||||
@ -384,6 +388,20 @@ class ChatActivity :
|
|||||||
private var lastRecordMediaPosition: Int = 0
|
private var lastRecordMediaPosition: Int = 0
|
||||||
private var lastRecordedSeeked: Boolean = false
|
private var lastRecordedSeeked: Boolean = false
|
||||||
|
|
||||||
|
private val audioFocusChangeListener = getAudioFocusChangeListener()
|
||||||
|
|
||||||
|
private val noisyAudioStreamReceiver = object : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context?, intent: Intent?) {
|
||||||
|
chatViewModel.isPausedDueToBecomingNoisy = true
|
||||||
|
if (isVoicePreviewPlaying) {
|
||||||
|
pausePreviewVoicePlaying()
|
||||||
|
}
|
||||||
|
if (currentlyPlayedVoiceMessage != null) {
|
||||||
|
pausePlayback(currentlyPlayedVoiceMessage!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private lateinit var participantPermissions: ParticipantPermissions
|
private lateinit var participantPermissions: ParticipantPermissions
|
||||||
|
|
||||||
private var videoURI: Uri? = null
|
private var videoURI: Uri? = null
|
||||||
@ -1099,7 +1117,9 @@ class ChatActivity :
|
|||||||
|
|
||||||
binding.messageInputView.micInputCloud.setOnClickListener {
|
binding.messageInputView.micInputCloud.setOnClickListener {
|
||||||
if (mediaRecorderState == MediaRecorderState.RECORDING) {
|
if (mediaRecorderState == MediaRecorderState.RECORDING) {
|
||||||
recorder?.stop()
|
audioFocusRequest(false) {
|
||||||
|
recorder?.stop()
|
||||||
|
}
|
||||||
mediaRecorderState = MediaRecorderState.INITIAL
|
mediaRecorderState = MediaRecorderState.INITIAL
|
||||||
stopMicInputRecordingAnimation()
|
stopMicInputRecordingAnimation()
|
||||||
showPreviewVoiceRecording(true)
|
showPreviewVoiceRecording(true)
|
||||||
@ -1328,8 +1348,11 @@ class ChatActivity :
|
|||||||
duration = it.duration.toLong()
|
duration = it.duration.toLong()
|
||||||
interpolator = LinearInterpolator()
|
interpolator = LinearInterpolator()
|
||||||
}
|
}
|
||||||
voicePreviewMediaPlayer!!.start()
|
audioFocusRequest(true) {
|
||||||
voicePreviewObjectAnimator!!.start()
|
voicePreviewMediaPlayer!!.start()
|
||||||
|
voicePreviewObjectAnimator!!.start()
|
||||||
|
handleBecomingNoisyBroadcast(register = true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setOnCompletionListener {
|
setOnCompletionListener {
|
||||||
@ -1343,15 +1366,21 @@ class ChatActivity :
|
|||||||
if (voicePreviewMediaPlayer == null) {
|
if (voicePreviewMediaPlayer == null) {
|
||||||
initPreviewVoiceRecording()
|
initPreviewVoiceRecording()
|
||||||
} else {
|
} else {
|
||||||
voicePreviewMediaPlayer!!.start()
|
audioFocusRequest(true) {
|
||||||
voicePreviewObjectAnimator!!.resume()
|
voicePreviewMediaPlayer!!.start()
|
||||||
|
voicePreviewObjectAnimator!!.resume()
|
||||||
|
handleBecomingNoisyBroadcast(register = true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun pausePreviewVoicePlaying() {
|
private fun pausePreviewVoicePlaying() {
|
||||||
Log.d(TAG, "paused preview voice recording")
|
Log.d(TAG, "paused preview voice recording")
|
||||||
voicePreviewMediaPlayer!!.pause()
|
audioFocusRequest(false) {
|
||||||
voicePreviewObjectAnimator!!.pause()
|
voicePreviewMediaPlayer!!.pause()
|
||||||
|
voicePreviewObjectAnimator!!.pause()
|
||||||
|
handleBecomingNoisyBroadcast(register = false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun stopPreviewVoicePlaying() {
|
private fun stopPreviewVoicePlaying() {
|
||||||
@ -1361,9 +1390,12 @@ class ChatActivity :
|
|||||||
voicePreviewObjectAnimator!!.end()
|
voicePreviewObjectAnimator!!.end()
|
||||||
voicePreviewObjectAnimator = null
|
voicePreviewObjectAnimator = null
|
||||||
binding.messageInputView.seekBar.clearAnimation()
|
binding.messageInputView.seekBar.clearAnimation()
|
||||||
voicePreviewMediaPlayer!!.stop()
|
audioFocusRequest(false) {
|
||||||
voicePreviewMediaPlayer!!.release()
|
voicePreviewMediaPlayer!!.stop()
|
||||||
voicePreviewMediaPlayer = null
|
voicePreviewMediaPlayer!!.release()
|
||||||
|
voicePreviewMediaPlayer = null
|
||||||
|
handleBecomingNoisyBroadcast(register = false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1817,6 +1849,73 @@ class ChatActivity :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getAudioFocusChangeListener(): AudioManager.OnAudioFocusChangeListener {
|
||||||
|
return AudioManager.OnAudioFocusChangeListener { flag ->
|
||||||
|
when (flag) {
|
||||||
|
AudioManager.AUDIOFOCUS_LOSS -> {
|
||||||
|
chatViewModel.isPausedDueToBecomingNoisy = false
|
||||||
|
if (isVoicePreviewPlaying) {
|
||||||
|
stopPreviewVoicePlaying()
|
||||||
|
}
|
||||||
|
if (currentlyPlayedVoiceMessage != null) {
|
||||||
|
stopMediaPlayer(currentlyPlayedVoiceMessage!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
|
||||||
|
chatViewModel.isPausedDueToBecomingNoisy = false
|
||||||
|
if (isVoicePreviewPlaying) {
|
||||||
|
pausePreviewVoicePlaying()
|
||||||
|
}
|
||||||
|
if (currentlyPlayedVoiceMessage != null) {
|
||||||
|
pausePlayback(currentlyPlayedVoiceMessage!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun audioFocusRequest(shouldRequestFocus: Boolean, onGranted: () -> Unit) {
|
||||||
|
if (chatViewModel.isPausedDueToBecomingNoisy) {
|
||||||
|
onGranted()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||||
|
val duration = AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
|
||||||
|
|
||||||
|
val isGranted: Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
val focusRequest = AudioFocusRequest.Builder(duration)
|
||||||
|
.setOnAudioFocusChangeListener(audioFocusChangeListener)
|
||||||
|
.build()
|
||||||
|
if (shouldRequestFocus) {
|
||||||
|
audioManager.requestAudioFocus(focusRequest)
|
||||||
|
} else {
|
||||||
|
audioManager.abandonAudioFocusRequest(focusRequest)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
@Deprecated("This method was deprecated in API level 26.")
|
||||||
|
if (shouldRequestFocus) {
|
||||||
|
audioManager.requestAudioFocus(audioFocusChangeListener, AudioManager.STREAM_MUSIC, duration)
|
||||||
|
} else {
|
||||||
|
audioManager.abandonAudioFocus(audioFocusChangeListener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isGranted == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
|
||||||
|
onGranted()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleBecomingNoisyBroadcast(register: Boolean) {
|
||||||
|
if (register && !chatViewModel.receiverRegistered) {
|
||||||
|
registerReceiver(noisyAudioStreamReceiver, IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY))
|
||||||
|
chatViewModel.receiverRegistered = true
|
||||||
|
} else if (!chatViewModel.receiverUnregistered) {
|
||||||
|
unregisterReceiver(noisyAudioStreamReceiver)
|
||||||
|
chatViewModel.receiverUnregistered = true
|
||||||
|
chatViewModel.receiverRegistered = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun startPlayback(message: ChatMessage) {
|
private fun startPlayback(message: ChatMessage) {
|
||||||
if (!active) {
|
if (!active) {
|
||||||
// don't begin to play voice message if screen is not visible anymore.
|
// don't begin to play voice message if screen is not visible anymore.
|
||||||
@ -1830,8 +1929,10 @@ class ChatActivity :
|
|||||||
|
|
||||||
mediaPlayer?.let {
|
mediaPlayer?.let {
|
||||||
if (!it.isPlaying) {
|
if (!it.isPlaying) {
|
||||||
it.start()
|
audioFocusRequest(true) {
|
||||||
Log.d(TAG, "MediaPlayer has Started")
|
it.start()
|
||||||
|
handleBecomingNoisyBroadcast(register = true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mediaPlayerHandler = Handler()
|
mediaPlayerHandler = Handler()
|
||||||
@ -1866,8 +1967,10 @@ class ChatActivity :
|
|||||||
|
|
||||||
private fun pausePlayback(message: ChatMessage) {
|
private fun pausePlayback(message: ChatMessage) {
|
||||||
if (mediaPlayer!!.isPlaying) {
|
if (mediaPlayer!!.isPlaying) {
|
||||||
mediaPlayer!!.pause()
|
audioFocusRequest(false) {
|
||||||
Log.d(TAG, "MediaPlayer is paused")
|
mediaPlayer!!.pause()
|
||||||
|
handleBecomingNoisyBroadcast(register = false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
message.isPlayingVoiceMessage = false
|
message.isPlayingVoiceMessage = false
|
||||||
@ -1925,7 +2028,10 @@ class ChatActivity :
|
|||||||
mediaPlayer?.let {
|
mediaPlayer?.let {
|
||||||
if (it.isPlaying) {
|
if (it.isPlaying) {
|
||||||
Log.d(TAG, "media player is stopped")
|
Log.d(TAG, "media player is stopped")
|
||||||
it.stop()
|
audioFocusRequest(false) {
|
||||||
|
it.stop()
|
||||||
|
handleBecomingNoisyBroadcast(register = false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: IllegalStateException) {
|
} catch (e: IllegalStateException) {
|
||||||
@ -2141,8 +2247,10 @@ class ChatActivity :
|
|||||||
private fun stopMicInputRecordingAnimation() {
|
private fun stopMicInputRecordingAnimation() {
|
||||||
if (micInputAudioRecordThread != null) {
|
if (micInputAudioRecordThread != null) {
|
||||||
Log.d(TAG, "Mic Animation Ended")
|
Log.d(TAG, "Mic Animation Ended")
|
||||||
micInputAudioRecorder.stop()
|
audioFocusRequest(false) {
|
||||||
micInputAudioRecorder.release()
|
micInputAudioRecorder.stop()
|
||||||
|
micInputAudioRecorder.release()
|
||||||
|
}
|
||||||
isMicInputAudioThreadRunning = false
|
isMicInputAudioThreadRunning = false
|
||||||
micInputAudioRecordThread = null
|
micInputAudioRecordThread = null
|
||||||
}
|
}
|
||||||
@ -2193,7 +2301,9 @@ class ChatActivity :
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
start()
|
audioFocusRequest(true) {
|
||||||
|
start()
|
||||||
|
}
|
||||||
mediaRecorderState = MediaRecorderState.RECORDING
|
mediaRecorderState = MediaRecorderState.RECORDING
|
||||||
Log.d(TAG, "recording started")
|
Log.d(TAG, "recording started")
|
||||||
} catch (e: IllegalStateException) {
|
} catch (e: IllegalStateException) {
|
||||||
@ -2234,8 +2344,10 @@ class ChatActivity :
|
|||||||
recorder?.apply {
|
recorder?.apply {
|
||||||
try {
|
try {
|
||||||
if (mediaRecorderState == MediaRecorderState.RECORDING) {
|
if (mediaRecorderState == MediaRecorderState.RECORDING) {
|
||||||
stop()
|
audioFocusRequest(false) {
|
||||||
reset()
|
stop()
|
||||||
|
reset()
|
||||||
|
}
|
||||||
mediaRecorderState = MediaRecorderState.INITIAL
|
mediaRecorderState = MediaRecorderState.INITIAL
|
||||||
Log.d(TAG, "stopped recorder")
|
Log.d(TAG, "stopped recorder")
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,11 @@ class ChatViewModel @Inject constructor(private val repository: ChatRepository)
|
|||||||
open class GetReminderExistState(val reminder: Reminder) : ViewState
|
open class GetReminderExistState(val reminder: Reminder) : ViewState
|
||||||
|
|
||||||
private val _getReminderExistState: MutableLiveData<ViewState> = MutableLiveData(GetReminderStartState)
|
private val _getReminderExistState: MutableLiveData<ViewState> = MutableLiveData(GetReminderStartState)
|
||||||
|
|
||||||
|
var isPausedDueToBecomingNoisy = false
|
||||||
|
var receiverRegistered = false
|
||||||
|
var receiverUnregistered = false
|
||||||
|
|
||||||
val getReminderExistState: LiveData<ViewState>
|
val getReminderExistState: LiveData<ViewState>
|
||||||
get() = _getReminderExistState
|
get() = _getReminderExistState
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@ import androidx.core.view.marginBottom
|
|||||||
import androidx.core.view.updateLayoutParams
|
import androidx.core.view.updateLayoutParams
|
||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import androidx.media3.common.AudioAttributes
|
||||||
import androidx.media3.common.MediaItem
|
import androidx.media3.common.MediaItem
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import androidx.media3.exoplayer.ExoPlayer
|
import androidx.media3.exoplayer.ExoPlayer
|
||||||
@ -165,7 +166,10 @@ class FullScreenMediaActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun initializePlayer() {
|
private fun initializePlayer() {
|
||||||
player = ExoPlayer.Builder(applicationContext).build()
|
player = ExoPlayer.Builder(applicationContext)
|
||||||
|
.setAudioAttributes(AudioAttributes.DEFAULT, true)
|
||||||
|
.setHandleAudioBecomingNoisy(true)
|
||||||
|
.build()
|
||||||
binding.playerView.player = player
|
binding.playerView.player = player
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,6 +206,7 @@ class FullScreenMediaActivity : AppCompatActivity() {
|
|||||||
windowInsetsController.show(WindowInsetsCompat.Type.systemBars())
|
windowInsetsController.show(WindowInsetsCompat.Type.systemBars())
|
||||||
supportActionBar?.show()
|
supportActionBar?.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun applyWindowInsets() {
|
private fun applyWindowInsets() {
|
||||||
val playerView = binding.playerView
|
val playerView = binding.playerView
|
||||||
val exoControls = playerView.findViewById<FrameLayout>(R.id.exo_bottom_bar)
|
val exoControls = playerView.findViewById<FrameLayout>(R.id.exo_bottom_bar)
|
||||||
|
Loading…
Reference in New Issue
Block a user