I try to use the following code to play music.
I know I need to release mediaPlayer as soon as possible if I don't use it again, so I place the release code in onCompletion.
1: Will onCompletion be launched after a music have finished play?
2: Will onCompletion be launched after I invoke mediaPlayer?.stop()?
3: Will onCompletion be launched if the Activity which invoke PlayHelper is destroied?
Code
class PlayHelper private constructor(): MediaPlayer.OnPreparedListener, MediaPlayer.OnCompletionListener {
private var mediaPlayer: MediaPlayer? = null
fun play(path: String){
mediaPlayer= MediaPlayer()
mediaPlayer?.setAudioAttributes(
AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build()
)
mediaPlayer?.setDataSource(path)
mediaPlayer?.setOnPreparedListener(this#PlayHelper)
mediaPlayer?.prepareAsync()
}
fun pause(){
mediaPlayer?.pause()
}
fun stop(){
mediaPlayer?.stop()
}
/** Called when MediaPlayer is ready */
override fun onPrepared(mediaPlayer: MediaPlayer) {
mediaPlayer.start()
}
override fun onCompletion(mediaPlayer: MediaPlayer) {
if (mediaPlayer.isPlaying) {
mediaPlayer.stop();
mediaPlayer.release()
}
}
companion object {
// For Singleton instantiation
#Volatile private var instance: PlayHelper? = null
fun getInstance() = instance?: synchronized(this) {
instance?: PlayHelper().also { instance = it }
}
}
}
Regarding the official documents, onCompletion is called only when the end of a media source is reached during playback. So, in other cases, like calling mediaPlayer.stop(), it won't be called.
1: Will onCompletion be launched after a music have finished play?
It will.
2: Will onCompletion be launched after I invoke mediaPlayer?.stop()?
It will not.
3: Will onCompletion be launched if the Activity which invoke PlayHelper is destroied?
It will not. The PlayHelper will be removed from memory when the Activity is destroyed.
This link: onCompletionListener from the docs contains the following line:
Called when the end of a media source is reached during playback.
Related
How Can i access to methods and finish my picture in picture activity from MainActivity
for example i want to pause videoplayer on pip mode from MainActivity:
VideoPlayerActivity().pauseVideo()
VideoPlayerActivity (Pip):
fun pauseVideo() {
player?.pause()
}
but it not working
I expected the video to stop but nothing happens
When you try to call VideoPlayerActivity().pauseVideo(), you are creating a new VideoPlayerActivity class and it will call that new class pauseVideo() instead of the original displaying class pauseVideo().
One suggestion can be creating a separate Singleton class, so that you can access your Player functions from any Activity.
class PlayerHelper {
companion object {
private var instance : PlayerHelper? = null
// Define your necessary Player tool
fun getInstance(): PlayerHelper {
if (instance == null) {
instance = PlayerHelper()
}
return instance!!
}
}
fun start() {
// Implement to start your player
Log.i("PlayerHelper", "start:")
}
fun pause() {
// Implement to pause your player
Log.i("PlayerHelper", "pause:")
}
}
And you can call pause() in any Activity like:
PlayerHelper.getInstance().pause()
Google cloud TTS audio does not stop after activity destroy.
googleCloudTTS = GoogleCloudTTSFactory.create(BuildConfig.API_KEY)
override fun onDestroy() {
destroyStory()
super.onDestroy()
}
// destroy story
private fun destroyStory() {
IS_PLAY_AUDIO = false
// googleCloudTTS?.stop()
googleCloudTTS?.close()
}
I tried with both googleCloudTTS?.close() and googleCloudTTS?.stop() but doesn't stop playing audio.
I have the following for loop.
for (song in songArray){
playSong(song)
}
and playSong as below:
fun playSong(song){
mediaPlayer.create(context, R.raw.song)
mediaPlayer.start
mediaPlayer?.setOnCompletionListener {
mediaPlayer!!.release()
mediaPlayer = null
}
}
The for loop does not wait for the entire playSong function to complete, and just immediately starts the next song. I want the the listener to be heard and the song to complete before iterating to the next song. If you could give me some guidance on this, I would appreciate it.
Here's how it could be done with coroutines. First, you need to create a suspend function version of playing media and waiting for it to finish. Since a completion listener is not a one-shot callback, I think it is more appropriate to use callbackFlow instead of suspendCoroutine.
suspend fun MediaPlayer.startAndAwait() {
callbackFlow {
setOnCompletionListener {
trySendBlocking(Unit)
}
awaitClose { setOnCompletionListener(null) }
}.first()
start()
}
Then you can use this function in a coroutine, so you can loop sequentially:
suspend fun playSong(song: Int){
MediaPlayer.create(context, song).apply {
mediaPlayer = this // so you can cancel playback from elsewhere
startAndAwait()
release()
mediaPlayer = null
}
}
//In a coroutine:
for (song in songArray){
playSong(song)
}
Instead of using for loop play songs from array when the previous one finishes
fun playSongArray(songArray: IntArray) {
var i = 0
mediaPlayer.create(context, R.raw.songArray[0])
mediaPlayer.start
mediaPlayer?.setOnCompletionListener {
if (i < songArray.size) {
mediaPlayer.create(context, R.raw.songArray[1])
mediaPlayer.start
i++
} else {
mediaPlayer!!.release()
mediaPlayer = null
}
}
}
So far I only found questions about how to make ExoPlayer keep playing when app goes to background. Why the hell is that the case by me without coding this bs??
This is what I have so far and it's inside RecyclerView OnBingViewHolder:
val player = ExoPlayer.Builder(context).build()
val mediaItem: MediaItem = MediaItem.fromUri(fileUrl)
player.setMediaItem(mediaItem)
player.repeatMode = Player.REPEAT_MODE_ONE
holder.vidPlayer.player = player
player.prepare()
player.seekTo(100)
// player.play()
holder.vidPlayer.setTag(mpTag, player)
holder.vidPlayer.setTag(manuelPlayTag, false)
holder.vidPlayer.setTag(manuelPauseTag, false)
player.addListener(object : Player.Listener { // player listener
override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
if (playWhenReady && playbackState == Player.STATE_READY) {
Log.d(tagg, "state: plays")
holder.vidPlayer.hideController()
} else if (playWhenReady) {
// might be idle (plays after prepare()),
// buffering (plays when data available)
// or ended (plays when seek away from end)
} else {
Log.d(tagg, "state: pause")
holder.vidPlayer.showController()
}
}
})
how I prevent the play when app goes to background?
When your app goes to the background the active Fragment/Activity's Lifecycle method onPause (and onStop) is called. In the onPause method you can cycle through your bound ViewHolders and stop the video player(s).
You can simply stop the ExoPlayer when the app goes to the background.
override fun onStop() {
super.onStop()
simpleExoPlayer.stop()
}
And in onStart just prepare() the ExoPlayer again:
override fun onStart() {
super.onStart()
simpleExoPlayer.prepare()
}
In order to automatically play the media, you need to set playWhenReady = true.
simpleExoPlayer.playWhenReady = true
By setting playWhenReady = true it will automcatically play the content and we don't need to explicitly call simpleExoPlayer.play().
I am new to programming and I am making a basic app for radio in which an introduction audio sounds when you press a button, then a second audio should appear until the network connection of the online radio is established.
I have managed to make the intro audio sound complete when I click, then silence is generated until the online radio plays, but I don't know how to put a second audio that detects the charging status before the radio plays. This is my code:
fun MediaPlayerRadio(){
mediaPlayer = MediaPlayer.create(
this#MainActivity,
Uri.parse("https://radiolink.com")
)
mediaPlayer?.start()
}
........................................................................
fun MediaPlayerIntroSound(){
mediaPlayer = MediaPlayer.create(this, R.raw.SoundIntro)
mediaPlayer?.start()
}
........................................................................
fun click_Button_Radio(){
btn.setOnClickListener(){
if (btn.isSelected){
btn.isSelected = false
mediaPlayer?.stop()
}else{
btn.isSelected = !btn.isSelected
MediaPlayerIntroSound()
mediaPlayer!!.setOnCompletionListener(object : MediaPlayer.OnCompletionListener {
override fun onCompletion(mp: MediaPlayer?) {
MediaPlayerRadio()
}
})
}
}
}
I hope you can support me with this.
For this, you will have to use 2 media players, one for intro sound and another for actual sound.
While the time actual sound media player gets ready, you can play different audios on the intro sound media player.
To stop the intro audio player once the other player is ready, you can make use of a view model.
//Play Media player
mediaPlayer = MediaPlayer.create(this#MainActivity, R.raw.first_audio)
mediaPlayer.start()
// Add completion listener, so that we can change the audio
// once the first one is finished
mediaPlayer.setOnCompletionListener {
mediaPlayer.release()
mediaPlayer = MediaPlayer.create(this#MainActivity, R.raw.second_audio)
mediaPlayer.start()
}
// We are observing a MutableLiveData of Boolean type,
// once it is true we will stop the intro media player
// and start the radio player
viewModel.isPlayerReady.observe(this) { isReady ->
if(isReady) {
mediaPlayer.stop()
// Start radio media player
}
}
// This is how I am changing the value of MutableLiveData
demoBtn.setOnClickListener {
viewModel.isPlayerReady.value = true
}
I have tested the above approach and it works fine as expected.
Steps to link a view model to the activity:
Add the dependency: implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
Make a view model class and a mutable live variable of type boolean in it.
class MainViewModel: ViewModel() {
val isPlayerReady = MutableLiveData<Boolean>()
}
Link view model with the activity.
class MainActivity : AppCompatActivity() {
private lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
....
}