androidkotlinandroid-fragmentsaudio-streamingexoplayer

Exoplayer in Audio Streaming Android App | exoplayer playing multiple audios from streaming url


This is my Fragment code being hosted on MainActivity:

class RadioPlayerFragment : Fragment() {

    private lateinit var player: SimpleExoPlayer
    private lateinit var mBinding: FragmentRadioPlayerBinding
    private var isPlaying: Boolean = false
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        mBinding = FragmentRadioPlayerBinding.inflate(inflater, container, false)
        return mBinding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        Log.d(TAG, "onViewCreated \n $isPlaying")
        isPlaying = SharedPrefs.getBoolean(requireContext(), "isPlaying")
        initializePlayer()
        updateUI()
        mBinding.btnPlayPause.setOnClickListener {
            if (isPlaying) {
                Log.d(TAG, "onViewCreated: $isPlaying pausing audio")
                pauseRadio()
            } else {
                Log.d(TAG, "onViewCreated: $isPlaying playing audio")
                playRadio()
            }
        }

        requireContext().registerReceiver(
            notificationActionReceiver, IntentFilter("ACTION_STOP")
        )
    }
    override fun onStart() {
        super.onStart()
        Log.d(TAG, "onStart")
        if (isPlaying) {
            Log.d(TAG, "onStart: isPlaying")
            player.playWhenReady = true
            updateUI()
            updateNotification()
        }
    }

    private fun initializePlayer() {
        if (::player.isInitialized && isPlaying) {
            player.stop()
            player.release()
        }

        player = SimpleExoPlayer.Builder(requireContext())
            .setTrackSelector(DefaultTrackSelector(requireContext()))
            .setLoadControl(DefaultLoadControl())
            .build()

             val mediaItem = MediaItem.fromUri(Uri.parse(Constants.k_STREAM_URL))

        player.setMediaItem(mediaItem)
        player.prepare()
        Log.d(TAG, "initializePlayer: $isPlaying")
        player.playWhenReady = false
    }
    private fun playRadio() {
        player.playWhenReady = true
        isPlaying = true
        SharedPrefs.setBoolean(requireContext(), "isPlaying", true)
        updateNotification()
        updateUI()
    }
    private fun pauseRadio() {
        player.playWhenReady = false
        isPlaying = false
        Log.d(TAG, "pauseRadio: $isPlaying")
        SharedPrefs.setBoolean(requireContext(), "isPlaying", false)
        updateNotification()
        updateUI()
    }
    private fun updateUI() {
        if (isPlaying) {
            mBinding.gif.setImageResource(R.drawable.vuplay)
            mBinding.btnPlayPause.setImageResource(R.drawable.stopbutton)
        } else {
            mBinding.gif.setImageResource(R.drawable.vustop)
            mBinding.btnPlayPause.setImageResource(R.drawable.playbutton)
        }
    }
    private fun updateNotification() {
        createNotificationChannel()

        val stopIntent = Intent(requireContext(), NotificationActionReceiver::class.java).apply {
            action = "ACTION_STOP"
        }
        val stopPendingIntent: PendingIntent = PendingIntent.getBroadcast(requireContext(), 0, stopIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)

        val notification = NotificationCompat.Builder(requireContext(), "radio_channel")
            .setContentTitle("GUGS FM is playing")
            .setContentText("App is running in the background")
            .setSmallIcon(R.drawable.logo_2)
            .addAction(R.drawable.stopbutton, "Stop", stopPendingIntent)
            .build()

        if (ActivityCompat.checkSelfPermission(
                requireContext(),
                Manifest.permission.POST_NOTIFICATIONS
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            return
        }
        NotificationManagerCompat.from(requireContext()).notify(1, notification)
    }
    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val name = "Radio Channel"
            val descriptionText = "Channel for radio notifications"
            val importance = NotificationManager.IMPORTANCE_LOW
            val channel = NotificationChannel("radio_channel", name, importance).apply {
                description = descriptionText
            }
            val notificationManager = requireContext().getSystemService(NotificationManager::class.java)
            notificationManager.createNotificationChannel(channel)
        }
    }

    private val notificationActionReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            if (intent?.action == "ACTION_STOP") {
                pauseRadio()
            }
        }
    }

    companion object{
        val TAG = "RadioPlayerFragment"
    }
}

The player is working fine when i use it on this fragment, my condition is that when i play the audio and navigate to another fragment it should keep playing, when i return back if it is playing then show the playing UI if it was stopped by me, then show the stopped UI.

But right now, what's happening is that when i navigate to another fragment and return back, it shows the relevant UI if playing then playing if paused then paused, but when i return from other fragment and try to stop the audio, it changes the UI by changing the button and gif, but audio does not stop, but in UI it stopped on frontend, now if the user clicks the button again to play, then it starts streaming another audio at a time 2 audio consectively, the old audio that didn't stop, and the new one now. This is happening only after navigation of fragment and returning back, otherwise start and stop is working fine.


Solution

  • Found the solution after searching and discussing with several android developers. The exoplayer should always be initialized in the Application level class, this will ensure that the player runs smoothly without creating several instances. Creating an application class for the app and initializing the exoplayer in there and then using using it in the fragment & notification receiver resolves my issue.