{"id":3752,"date":"2025-04-29T12:00:29","date_gmt":"2025-04-29T12:00:29","guid":{"rendered":"https:\/\/alsaeeddev.com\/?p=3752"},"modified":"2025-09-05T11:30:38","modified_gmt":"2025-09-05T11:30:38","slug":"media-player-in-android-studio-kotlin","status":"publish","type":"post","link":"https:\/\/alsaeeddev.com\/shop\/media-player-in-android-studio-kotlin\/","title":{"rendered":"Media Player in Android Studio Using Kotlin | Media Player Source Code"},"content":{"rendered":"<p>If you&#8217;re looking to create a simple <strong>media player in Android Studio using Kotlin<\/strong>, this article will guide you through the core of your project: the <code>MainActivity<\/code>. This is where permissions are handled, songs are loaded from device storage, and user interactions are managed.<\/p>\n<h2>Overview of Media Player MainActivity<\/h2>\n<p>The <strong>MainActivity<\/strong> is responsible for:<\/p>\n<ul>\n<li>Checking and requesting runtime permissions<\/li>\n<li>Fetching music files from the device storage<\/li>\n<li>Binding to the MusicService for background playback<\/li>\n<li>Handling user interactions like play, pause, next, and previous<\/li>\n<\/ul>\n<h3>Kotlin Code Breakdown for Media Player<\/h3>\n<p>The class implements <code>AppCompatActivity<\/code> and <code>ServiceConnection<\/code> to establish communication with the background music service. It uses <code>ActivityMainBinding<\/code> for UI binding and coroutines to prevent blocking the main thread while querying songs from <code>MediaStore<\/code>.<\/p>\n<h3>Permission Handling for Media Player<\/h3>\n<p>For Android 13+ (Tiramisu), it requests <code>READ_MEDIA_AUDIO<\/code>. For earlier versions, <code>READ_EXTERNAL_STORAGE<\/code> is used.<\/p>\n<h3>Loading Songs<\/h3>\n<p>The app queries <code>MediaStore.Audio.Media<\/code> using a coroutine and populates the <code>songsList<\/code> if the files exist.<\/p>\n<h3>Binding Media Player to MusicService<\/h3>\n<p>On successful service connection, the list is passed to the service and the UI is updated with the currently playing song title.<\/p>\n<h3>Media Player Controls and Playback<\/h3>\n<p>The play\/pause, next, and previous buttons update the state using <code>MusicService<\/code>. The UI is automatically refreshed using <code>updateControls()<\/code>.<\/p>\n<h3>Other Media Player App Classes<\/h3>\n<p>To complete the media player, create the following Kotlin classes and paste their code accordingly:<\/p>\n<ul>\n<li><strong>MusicService.kt<\/strong> &#8211; Manages playback in the background<\/li>\n<li><strong>AudioModel.kt<\/strong> &#8211; Data class representing a song<\/li>\n<li><strong>MusicAdapter.kt<\/strong> &#8211; Binds song data to RecyclerView<\/li>\n<li><strong>MusicPlayerActivity.kt<\/strong> &#8211; Full screen music controls<\/li>\n<\/ul>\n<h4>1. MainActivity.kt<\/h4>\n<pre><code>\r\n  class MainActivity : AppCompatActivity(), ServiceConnection {\r\n    private var musicService: MusicService? = null\r\n    private var isServiceBound = false\r\n    private lateinit var binding: ActivityMainBinding\r\n    private val songsList: MutableList = mutableListOf()\r\n    private lateinit var adapter: MusicAdapter\r\n    private var isPlaying = false\r\n\r\n    override fun onCreate(savedInstanceState: Bundle?) {\r\n        super.onCreate(savedInstanceState)\r\n        binding = ActivityMainBinding.inflate(layoutInflater)\r\n        setContentView(binding.root)\r\n        window.statusBarColor = Color.WHITE\r\n\r\n        if (!checkPermission()) {\r\n            requestPermission()\r\n\r\n        } else {\r\n            initializeApp()\r\n        }\r\n\r\n    }\r\n\r\n\r\n    private fun initializeApp() {\r\n        \/\/ Launch coroutine on Main thread\r\n        CoroutineScope(Dispatchers.Main).launch {\r\n\r\n            \/\/ Show progress bar\r\n            binding.progressBar.visibility = View.VISIBLE\r\n\r\n            val songs = withContext(Dispatchers.IO) {\r\n                val songs = ArrayList()\r\n                val projection = arrayOf(\r\n                    MediaStore.Audio.Media.TITLE,\r\n                    MediaStore.Audio.Media.DATA,\r\n                    MediaStore.Audio.Media.DURATION\r\n                )\r\n                val selection = MediaStore.Audio.Media.IS_MUSIC + \" !=0\"\r\n\r\n                contentResolver.query(\r\n                    MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,\r\n                    projection,\r\n                    selection,\r\n                    null,\r\n                    null\r\n                )?.use { cursor -&gt;\r\n                    while (cursor.moveToNext()) {\r\n                        val audioModel = AudioModel(\r\n                            cursor.getString(1),\r\n                            cursor.getString(0),\r\n                            cursor.getString(2)\r\n                        )\r\n                        if (File(audioModel.path).exists()) {\r\n                            songs.add(audioModel)\r\n                        }\r\n                    }\r\n                }\r\n                songs\r\n            }\r\n\r\n            \/\/ Hide progress bar\r\n            binding.progressBar.visibility = View.GONE\r\n\r\n            songsList.clear()\r\n            songsList.addAll(songs)\r\n\r\n            adapter = MusicAdapter(this@MainActivity, songsList) { position -&gt;\r\n                if (isPlaying) {\r\n                    musicService?.musicPlay(songsList)\r\n                    binding.songTitle.text = songsList[MyMediaPlayer.currentIndex].title\r\n                    adapter.notifyDataSetChanged()\r\n                } else {\r\n\r\n                    if (songsList.isNotEmpty()) {\r\n                        val intent = Intent(this@MainActivity, MusicPlayerActivity::class.java)\r\n                        intent.putExtra(\"List\", songsList as Serializable)\r\n                        intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK\r\n                        startActivity(intent)\r\n                    } else {\r\n                        Toast.makeText(this@MainActivity, \"No songs to play\", Toast.LENGTH_SHORT)\r\n                            .show()\r\n                    }\r\n\r\n                }\r\n            }\r\n\r\n            binding.recyclerView.layoutManager = LinearLayoutManager(this@MainActivity)\r\n            binding.recyclerView.adapter = adapter\r\n            binding.noSongFound.visibility = if (songsList.isEmpty()) View.VISIBLE else View.GONE\r\n\r\n            setupControls()\r\n        }\r\n    }\r\n\r\n\r\n    private fun setupControls() {\r\n        val intent = Intent(this, MusicService::class.java)\r\n        bindService(intent, this, Context.BIND_AUTO_CREATE)\r\n\r\n        binding.pausePlay.setOnClickListener {\r\n            musicService?.getMusicPlayer()?.let {\r\n                if (it.isPlaying) {\r\n                    it.pause()\r\n                    binding.pausePlay.setImageResource(R.drawable.play_ic)\r\n                } else {\r\n                    it.start()\r\n                    binding.pausePlay.setImageResource(R.drawable.pause_ic)\r\n                }\r\n            }\r\n        }\r\n\r\n\r\n\r\n        binding.next.setOnClickListener { musicNext() }\r\n        binding.previous.setOnClickListener { musicPrevious() }\r\n\r\n        binding.controls.setOnClickListener {\r\n            val intent1 = Intent(this@MainActivity, MusicPlayerActivity::class.java)\r\n            intent1.putExtra(\"IS\", true)\r\n            intent1.putExtra(\"List\", songsList as Serializable)\r\n            startActivity(intent1)\r\n        }\r\n    }\r\n\r\n\r\n    private fun musicNext() {\r\n\r\n        musicService?.next()\r\n        binding.songTitle.text = songsList[MyMediaPlayer.currentIndex].title\r\n        adapter.notifyDataSetChanged()\r\n\r\n    }\r\n\r\n\r\n    private fun musicPrevious() {\r\n        musicService?.previous()\r\n        binding.songTitle.text = songsList[MyMediaPlayer.currentIndex].title\r\n        adapter.notifyDataSetChanged()\r\n\r\n    }\r\n\r\n\r\n    private fun checkPermission(): Boolean {\r\n\r\n        val result = ContextCompat.checkSelfPermission(\r\n            this@MainActivity,\r\n            Manifest.permission.READ_EXTERNAL_STORAGE\r\n        )\r\n\r\n        if (result == PackageManager.PERMISSION_GRANTED) {\r\n            return true\r\n\r\n        } else {\r\n            return false\r\n        }\r\n    }\r\n\r\n\r\n    private fun requestPermission() {\r\n        val permission = Manifest.permission.READ_EXTERNAL_STORAGE\r\n\r\n        if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.TIRAMISU) {\r\n            \/\/ For Android 13+ (Tiramisu), use READ_MEDIA_AUDIO instead\r\n            ActivityCompat.requestPermissions(\r\n                this,\r\n                arrayOf(Manifest.permission.READ_MEDIA_AUDIO),\r\n                92\r\n            )\r\n        } else {\r\n\r\n            ActivityCompat.requestPermissions(\r\n                this,\r\n                arrayOf(permission),\r\n                92\r\n            )\r\n\r\n        }\r\n    }\r\n\r\n\r\n    override fun onRequestPermissionsResult(\r\n        requestCode: Int,\r\n        permissions: Array,\r\n        grantResults: IntArray\r\n    ) {\r\n        super.onRequestPermissionsResult(requestCode, permissions, grantResults)\r\n\r\n        if (requestCode == 92 &amp;&amp; grantResults.isNotEmpty() &amp;&amp; grantResults[0] == PackageManager.PERMISSION_GRANTED) {\r\n            Toast.makeText(this, \"Permission granted\", Toast.LENGTH_SHORT).show()\r\n            initializeApp() \/\/ Or load your music files\r\n        } else {\r\n            Toast.makeText(this, \"Permission denied\", Toast.LENGTH_SHORT).show()\r\n            requestPermission()\r\n        }\r\n    }\r\n\r\n\r\n    override fun onServiceConnected(name: ComponentName?, service: IBinder?) {\r\n        val binder = service as MusicService.MyBinder\r\n        musicService = binder.currentService()\r\n        isServiceBound = true\r\n        musicService!!.setSongLIst(songsList)\r\n\r\n        \/\/ Update the controls based on the current state of the media player\r\n        updateControls()\r\n    }\r\n\r\n    override fun onServiceDisconnected(name: ComponentName?) {\r\n        musicService = null\r\n        isServiceBound = false\r\n    }\r\n\r\n\r\n    private fun updateControls() {\r\n        musicService?.getMusicPlayer()?.let { mediaPlayer -&gt;\r\n            if (mediaPlayer.isPlaying) {\r\n                binding.controls.visibility = View.VISIBLE\r\n                binding.songTitle.text = songsList[MyMediaPlayer.currentIndex].title\r\n                binding.pausePlay.setImageResource(R.drawable.pause_ic)\r\n                isPlaying = true\r\n            } else {\r\n                isPlaying = false\r\n                binding.controls.visibility = View.GONE\r\n            }\r\n\r\n            mediaPlayer.setOnCompletionListener {\r\n                musicNext() \/\/ Move to the next song when the current song is completed.\r\n            }\r\n        }\r\n\r\n    }\r\n\r\n    @SuppressLint(\"NotifyDataSetChanged\")\r\n    override fun onResume() {\r\n        super.onResume()\r\n        if (::adapter.isInitialized) {\r\n            adapter.notifyDataSetChanged()\r\n            updateControls()\r\n        }\r\n\r\n    }\r\n\r\n    override fun onDestroy() {\r\n        super.onDestroy()\r\n        if (isServiceBound) {\r\n            unbindService(this)\r\n            isServiceBound = false\r\n        }\r\n    }\r\n}\r\n<\/code><\/pre>\n<hr \/>\n<h4>2. MusicService.kt<\/h4>\n<pre><code>\r\nclass MusicService : Service() {\r\n\r\n    private var myBinder = MyBinder()\r\n    private var mediaPlayer: MediaPlayer? = MyMediaPlayer.getInstance()\r\n    private var currentSongPath: String? = null\r\n    private var isAlreadyPlaying = false\r\n    private var songsList: List? = null\r\n\r\n\r\n    fun setSongLIst(list: List) {\r\n        songsList = list\r\n\r\n    }\r\n\r\n    public fun getMusicPlayer(): MediaPlayer? {\r\n        return mediaPlayer\r\n    }\r\n\r\n\r\n    override fun onBind(intent: Intent?): IBinder? {\r\n        return myBinder\r\n\r\n    }\r\n\r\n\r\n    fun musicPlay(songList: List) {\r\n        currentSongPath = songList[MyMediaPlayer.currentIndex].path\r\n        mediaPlayer!!.reset()\r\n        mediaPlayer!!.setDataSource(currentSongPath)\r\n        mediaPlayer!!.prepare()\r\n        mediaPlayer!!.start()\r\n\r\n    }\r\n\r\n\r\n    fun pause() {\r\n        mediaPlayer?.pause()\r\n        Log.d(\"MyService\", \"Pausing music\")\r\n    }\r\n\r\n    fun stop() {\r\n        mediaPlayer?.stop()\r\n        Log.d(\"MyService\", \"Stopping music\")\r\n    }\r\n\r\n\r\n    fun next() {\r\n        if (MyMediaPlayer.currentIndex == songsList!!.size - 1) {\r\n            return\r\n        }\r\n\r\n        MyMediaPlayer.currentIndex += 1\r\n        mediaPlayer!!.reset()\r\n        if (songsList != null) {\r\n            musicPlay(songsList!!)\r\n        }\r\n        isAlreadyPlaying = false\r\n\r\n\r\n    }\r\n\r\n\r\n    fun previous() {\r\n        if (MyMediaPlayer.currentIndex == 0) {\r\n            return\r\n        }\r\n        MyMediaPlayer.currentIndex -= 1\r\n        mediaPlayer!!.reset()\r\n        if (songsList != null) {\r\n            musicPlay(songsList!!)\r\n        }\r\n        isAlreadyPlaying = false\r\n\r\n    }\r\n\r\n\r\n    fun getPlayingStatus(): Boolean {\r\n        return isAlreadyPlaying\r\n    }\r\n\r\n\r\n    fun isPlaying(): Boolean {\r\n        return mediaPlayer?.isPlaying ?: false\r\n    }\r\n\r\n    fun getCurrentPosition(): Int {\r\n        return mediaPlayer?.currentPosition ?: 0\r\n    }\r\n\r\n    fun seekTo(position: Int) {\r\n        mediaPlayer?.seekTo(position)\r\n    }\r\n\r\n\r\n    override fun onDestroy() {\r\n        super.onDestroy()\r\n    }\r\n\r\n\r\n    inner class MyBinder : Binder() {\r\n        fun currentService(): MusicService {\r\n            return this@MusicService\r\n        }\r\n    }\r\n\r\n}\r\n<\/code><\/pre>\n<h4>3. MusicAdapter.kt<\/h4>\n<pre><code>\r\nclass MusicAdapter(\r\n    private val context: Context,\r\n    private val songList: List,\r\n    private val onClick: (Int) -&gt; Unit\r\n) : RecyclerView.Adapter() {\r\n\r\n\r\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemHolder {\r\n        val view = SongItemBinding.inflate(LayoutInflater.from(context), parent, false)\r\n        return ItemHolder(view)\r\n    }\r\n\r\n    override fun getItemCount(): Int {\r\n        return songList.size\r\n    }\r\n\r\n\r\n    public fun getSongLIst(): List {\r\n        return songList\r\n    }\r\n\r\n    override fun onBindViewHolder(holder: ItemHolder, position: Int) {\r\n        val audioSong = songList[position]\r\n        holder.bind(audioSong, position)\r\n    }\r\n\r\n\r\n    inner class ItemHolder(private val binding: SongItemBinding) :\r\n        RecyclerView.ViewHolder(binding.root) {\r\n\r\n\r\n        fun bind(audioSong: AudioModel, position: Int) {\r\n\r\n            if (MyMediaPlayer.currentIndex == position) {\r\n                binding.tvSongTitle.setTextColor(Color.RED)\r\n            } else {\r\n                binding.tvSongTitle.setTextColor(Color.BLACK)\r\n            }\r\n            binding.tvSongTitle.text = audioSong.title\r\n            \/\/   binding.ivMusic.setImageResource(R.drawable.music_ic)\r\n\r\n            binding.root.setOnClickListener {\r\n                MyMediaPlayer.getInstance().reset()\r\n                MyMediaPlayer.currentIndex = adapterPosition\r\n                onClick(adapterPosition)\r\n\r\n\r\n            }\r\n\r\n        }\r\n\r\n    }\r\n}\r\n<\/code><\/pre>\n<hr \/>\n<p><iframe loading=\"lazy\" title=\"Android Media Player App in Android Studio | Music Player Android | #shorts #androidappdevelopment\" width=\"640\" height=\"360\" src=\"https:\/\/www.youtube.com\/embed\/y8YfsAPNYWU?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe><\/p>\n<hr \/>\n<p><a href=\"https:\/\/github.com\/alsaeeddev\/MusicPlayer\/archive\/refs\/heads\/main.zip\">Download Source Code<\/a><\/p>\n<h2>Conclusion<\/h2>\n<p>This <strong>Android media player in Kotlin<\/strong> is lightweight, efficient, and follows best practices for modern development using ViewBinding, coroutines, and service components. Add more features like notifications, shuffle, or repeat to further enhance your app.<\/p>\n<p>You can also Check Out the official Android Media Player<a href=\"https:\/\/developer.android.com\/reference\/android\/media\/MediaPlayer\" target=\"_blank\" rel=\"noopener\"><strong> Documentation<\/strong><\/a>.<br \/>\nWant to learn more? Visit our tutorials at <a href=\"https:\/\/alsaeeddev.com\/blog\"><strong>Blog<\/strong><\/a> .<\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you&#8217;re looking to create a simple media player in Android Studio using Kotlin, this article will guide you through the core of your project: the MainActivity. This is where permissions are handled, songs are loaded from device storage, and user interactions are managed. Overview of Media Player MainActivity The MainActivity is responsible for: Checking [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":3830,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1,116,164],"tags":[22,127,126,119,124,122,131,129,128,121,125,118,120,130,123],"class_list":["post-3752","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-android","category-kotlin","category-source-codes","tag-android-app-development","tag-android-kotlin-project","tag-android-media-playback","tag-android-media-player","tag-android-music-player","tag-android-studio-kotlin","tag-android-studio-tutorial","tag-build-media-player-kotlin","tag-kotlin-media-playback","tag-kotlin-media-player","tag-kotlin-tutorial","tag-media-player","tag-media-player-in-android-studio","tag-media-player-source-code","tag-music-player-app"],"_links":{"self":[{"href":"https:\/\/alsaeeddev.com\/shop\/wp-json\/wp\/v2\/posts\/3752","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/alsaeeddev.com\/shop\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/alsaeeddev.com\/shop\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/alsaeeddev.com\/shop\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/alsaeeddev.com\/shop\/wp-json\/wp\/v2\/comments?post=3752"}],"version-history":[{"count":29,"href":"https:\/\/alsaeeddev.com\/shop\/wp-json\/wp\/v2\/posts\/3752\/revisions"}],"predecessor-version":[{"id":4035,"href":"https:\/\/alsaeeddev.com\/shop\/wp-json\/wp\/v2\/posts\/3752\/revisions\/4035"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/alsaeeddev.com\/shop\/wp-json\/wp\/v2\/media\/3830"}],"wp:attachment":[{"href":"https:\/\/alsaeeddev.com\/shop\/wp-json\/wp\/v2\/media?parent=3752"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/alsaeeddev.com\/shop\/wp-json\/wp\/v2\/categories?post=3752"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/alsaeeddev.com\/shop\/wp-json\/wp\/v2\/tags?post=3752"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}