11package com.audiowaveform
22
3+ import android.content.Context
4+ import android.media.AudioAttributes
5+ import android.media.AudioFocusRequest
6+ import android.media.AudioManager
37import android.net.Uri
8+ import android.os.Build
49import android.os.CountDownTimer
510import com.facebook.react.bridge.Arguments
611import com.facebook.react.bridge.Promise
@@ -18,13 +23,93 @@ class AudioPlayer(
1823) {
1924 private val appContext = context
2025 private lateinit var player: ExoPlayer
26+ private var audioManager: AudioManager = appContext.getSystemService(Context .AUDIO_SERVICE ) as AudioManager
27+ private var audioFocusRequest: AudioFocusRequest ? = null
2128 private var playerListener: Player .Listener ? = null
2229 private var isPlayerPrepared: Boolean = false
2330 private var finishMode = FinishMode .Stop
2431 private val key = playerKey
2532 private var updateFrequency = UpdateFrequency .Low
2633 private lateinit var audioPlaybackListener: CountDownTimer
2734 private var isComponentMounted = true // Flag to track mounting status
35+ private var isAudioFocusGranted= false
36+
37+ init {
38+ // Set up the audio focus request
39+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .O ) {
40+ audioFocusRequest = AudioFocusRequest .Builder (AudioManager .AUDIOFOCUS_GAIN )
41+ .setAudioAttributes(
42+ AudioAttributes .Builder ()
43+ .setUsage(AudioAttributes .USAGE_MEDIA )
44+ .setContentType(AudioAttributes .CONTENT_TYPE_MUSIC )
45+ .build()
46+ ).setAcceptsDelayedFocusGain(true )
47+ .setOnAudioFocusChangeListener { focusChange ->
48+ handleAudioFocusChange(focusChange)
49+ }
50+ .build()
51+ }
52+ }
53+
54+ private fun handleAudioFocusChange (focusChange : Int ) {
55+ when (focusChange) {
56+ AudioManager .AUDIOFOCUS_GAIN -> {
57+ // Audio focus granted; resume playback if necessary
58+ if (! player.isPlaying) {
59+ player.play()
60+ }
61+ player.volume = 1.0f // Restore full volume
62+ }
63+ AudioManager .AUDIOFOCUS_LOSS -> {
64+ // Permanent loss of audio focus; pause playback
65+ if (player.isPlaying) {
66+ val args: WritableMap = Arguments .createMap()
67+ stopListening()
68+ player.pause()
69+ abandonAudioFocus()
70+ args.putInt(Constants .finishType, 1 )
71+ args.putString(Constants .playerKey, key)
72+ appContext.getJSModule(DeviceEventManagerModule .RCTDeviceEventEmitter ::class .java)?.emit(" onDidFinishPlayingAudio" , args)
73+ }
74+ }
75+ AudioManager .AUDIOFOCUS_LOSS_TRANSIENT -> {
76+ // Temporary loss of audio focus; pause playback
77+ if (player.isPlaying) {
78+ player.pause()
79+ }
80+ }
81+ AudioManager .AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
82+ // Temporarily loss of audio focus; but can continue playing at a lower volume.
83+ player.volume = 0.2f
84+ }
85+ }
86+ }
87+
88+ private fun requestAudioFocus (): Boolean {
89+ return if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .O ) {
90+ audioFocusRequest?.let {
91+ val result = audioManager.requestAudioFocus(it)
92+ isAudioFocusGranted = result == AudioManager .AUDIOFOCUS_REQUEST_GRANTED
93+ return isAudioFocusGranted
94+ } ? : false
95+ } else {
96+ val result = audioManager.requestAudioFocus(
97+ { focusChange -> handleAudioFocusChange(focusChange) },
98+ AudioManager .STREAM_MUSIC ,
99+ AudioManager .AUDIOFOCUS_GAIN
100+ )
101+ isAudioFocusGranted = result == AudioManager .AUDIOFOCUS_REQUEST_GRANTED
102+ return isAudioFocusGranted
103+ }
104+ }
105+
106+ private fun abandonAudioFocus () {
107+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .O ) {
108+ audioManager.abandonAudioFocusRequest(audioFocusRequest!! )
109+ }
110+ isAudioFocusGranted = false
111+ }
112+
28113
29114 fun markPlayerAsUnmounted () {
30115 isComponentMounted = false
@@ -134,12 +219,15 @@ class AudioPlayer(
134219 this .finishMode = FinishMode .Stop
135220 }
136221
137- validateAndSetPlaybackSpeed(player, speed)
138-
139- player.playWhenReady = true
140- player.play()
141- promise.resolve(true )
142- startListening(promise)
222+ validateAndSetPlaybackSpeed(player, speed)
223+ if (requestAudioFocus()) {
224+ player.playWhenReady = true
225+ player.play()
226+ promise.resolve(true )
227+ startListening(promise)}
228+ else {
229+ promise.reject(" AudioFocusError" , " Failed to gain audio focus" )
230+ }
143231 } catch (e: Exception ) {
144232 promise.reject(" Can not start the player" , e.toString())
145233 }
@@ -153,15 +241,17 @@ class AudioPlayer(
153241 isPlayerPrepared = false
154242 player.stop()
155243 player.release()
244+ abandonAudioFocus()
156245 }
157246
158- fun pause (promise : Promise ) {
247+ fun pause (promise : Promise ? ) {
159248 try {
160249 stopListening()
161250 player.pause()
162- promise.resolve(true )
251+ abandonAudioFocus()
252+ promise?.resolve(true )
163253 } catch (e: Exception ) {
164- promise.reject(" Failed to pause the player" , e.toString())
254+ promise? .reject(" Failed to pause the player" , e.toString())
165255 }
166256
167257 }
0 commit comments