I have exoplayer integrated within my application based on this link.
I have added a pending intent inside createCurrentContentIntent().
return PendingIntent.getActivity(
context, 0,
Intent(context, MyActivity::class.java), 0
)
I face an issue over here. I started playing the audio and the player notification also comes up in the status bar. My requirement is to play audio even if the app is in the background. So, I haven't released the player in onStop(). I have added the below code in onDestroy().
override fun onDestroy() {
playerNotificationManager?.setPlayer(null)
player?.stop()
player?.release()
player = null
super.onDestroy()
}
If I manually kill the application from the background when the player is playing, the notification doesn't go off. So, if I click on the notification it will crash with NullPointerException because MyActivtity is no more.
Could someone suggest a solution for the same?
I've implemented ExoPlayer along with MediaSessionCompat and MediaSessionConnector, which allows Exo to manage the media notification (and stuff like audio focus) implicitly.
class MyServiceClass {
private lateinit var player: SimpleExoPlayer
private lateinit var playerNotificationManager: PlayerNotificationManager
private lateinit var mediaSession: MediaSessionCompat
private lateinit var mediaSessionConnector: MediaSessionConnector
override fun onCreate() {
super.onCreate()
player = ...
playerNotificationManager = ...
mediaSession = MediaSessionCompat(this, CONSTANT).apply{ ..setup callback... }
mediaSessionConnector = MediaSessionConnector(mediaSession)
mediaSessionConnector.setPlayer(player)
playerNotificationManager.setMediaSessionToken(mediaSession.token)
playerNotificationManager.setPlayer(player)
}
override fun onDestroy() {
mediaSession.isActive = false
mediaSession.release()
mediaSessionConnector.setPlayer(null)
playerNotificationManager.setPlayer(null)
player.release()
super.onDestroy()
}
}
This should handle removing the notification, when you kill the app.
I also use a PlayerNotificationReceiver to react to notification changes by the system which is omitted in the code above. Also the whole part of triggering and reacting to notifications in the app is omitted.
I changed the complete implementation and used services. This solved the issue.
Related
I'm new to android development, I'm building an application that supports playing video in the background. I decided to do it through Exo Player and PlayerNotificationManager. On Android 12 and below, everything works fine, but on Android 13, the notification does not want to be displayed at all, any ideas?
I use MVVM architecture, in my viewmodel's init method I initialize exoPlayer and playerNotificationManager
initialization code:
playerNotificationManager = PlayerNotificationManager.Builder(
getApplication<Application>().applicationContext,
1,
EXO_PLAYER_CHANNEL_ID)
.setSmallIconResourceId(R.drawable.racoon)
.setMediaDescriptionAdapter(mediaDescriptionAdapter)
.setFastForwardActionIconResourceId(R.drawable.ic_forward_10sec)
.setRewindActionIconResourceId(R.drawable.ic_replay_10sec)
.build()
After that, in my fragment, I assign exoPlayer to my playerNotificationManager in the onResume and onStop methods:
override fun onResume() {
super.onResume()
Log.d("MyLog", "onResume")
if (videoViewModel.playerNotificationManager != null){
videoViewModel.playerNotificationManager?.setPlayer(null)
}
}
override fun onStop() {
super.onStop()
Log.d("MyLog", "onStop")
videoViewModel.playerNotificationManager?.setPlayer(videoViewModel.exoPlayer)
}
Also I tried to register the following permissions in my manifest:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
But the result remained the same, android 13 does not want to display my notification at all
Based on #primo comment and on this links:
How do I request push notification permissions for android 13?
https://developer.android.com/training/permissions/requesting#request-permission
https://developer.android.com/develop/ui/views/notifications/notification-permission#best-practices
I did the following to solve my problem:
In onCreate method of my fragment:
if (VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) {
val launcher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted: Boolean? ->
notificationGranted = isGranted == true
}
launcher.launch(Manifest.permission.POST_NOTIFICATIONS)
}
In onStop method:
if(VERSION.SDK_INT >= VERSION_CODES.TIRAMISU && !notificationGranted){
videoViewModel.exoPlayer.pause()
}
videoViewModel.playerNotificationManager?.setPlayer(videoViewModel.exoPlayer)
And then everything works fine.
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.
In an Android app made to play some audio file in the background, I have the following situation. The app plays the audio as I expect, also keeping playing in the background. The issue I am facing is when I want to stop the service.
I have two buttons on the main activity, one for starting the service and the other one to stop it.
This is the function fired by the START button:
fun startHandler(view: View) {
val audioName = "myAudio"
val serviceIntent = Intent(this, TheService::class.java)
serviceIntent.putExtra("name", audioName);
startForegroundService(serviceIntent)
}
This is the function fired by the STOP button:
fun stopHandler(view: View) {
val serviceIntent = Intent(this, TheService::class.java)
stopService(serviceIntent)
}
When I tap the STOP button, the audio stops (as expected), but the app crashes right after (this is not expected).
Here is the onDestroy() function on the service side:
override fun onDestroy() {
super.onDestroy()
audioPlayer.stop()
}
And the onStartCommand() function on the service side:
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val runnable = Runnable {
val audioName = intent?.getStringExtra("name")
val audioID = resources.getIdentifier(audioName,"raw", packageName)
audioPlayer = MediaPlayer.create(this, audioID)
audioPlayer?.setLooping(true)
audioPlayer.start()
}
val thread = Thread(runnable)
thread.start()
return START_NOT_STICKY
}
Is there any mistake that can be seen in the code above or some place I should look at in order to solve the problem ?
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)
....
}