This guide will walk you through creating a music player app using Android and Kotlin. The app will allow users to play music files from the external/internal storage of their phones and include features such as play/pause, forward/backward, loop/repeat, a draggable progress bar, and additional custom features.
- Project Setup
- Create a New Android Project
- Add Necessary Dependencies
- Understanding the Key APIs and Libraries
- MediaPlayer
- MediaStore
- SeekBar
- Handler
- SharedPreferences
- Designing the UI
- Layout Design (XML)
- Adding Buttons and SeekBar
- Implementing Core Features
- Accessing Music Files
- Initializing MediaPlayer
- Implementing Play/Pause Functionality
- Forward/Backward Functionality
- Loop/Repeat Functionality
- Draggable Progress Bar
- Extra Custom Features
- Handling Audio Focus
- Saving User Preferences
- Customizing UI Elements
- Final Touches
- Testing
- Debugging
- Packaging and Deployment
- Open Android Studio.
- Click on "Create New Project."
- Choose "Empty Activity" and click "Next."
- Name your project (e.g.,
MusicPlayerApp). - Select Kotlin as the programming language.
- Finish the setup.
You might need to add some dependencies for enhanced features or UI components. Open build.gradle (Module: app) and add the following dependencies:
dependencies {
implementation ("androidx.core:core-ktx:1.7.0")
implementation ("androidx.appcompat:appcompat:1.4.0")
implementation ("com.google.android.material:material:1.4.0")
// Additional dependencies for custom features if needed
}Sync the project to apply the changes.
- The
MediaPlayerclass is the core of this project. It handles audio playback from various sources like external/internal storage. - You can control playback (start, stop, pause) and listen to different events using listeners.
MediaStoreis used to retrieve and organize multimedia content. It provides a standardized way to access audio files stored on the device.
SeekBaris the UI component that allows users to track the progress of a song and jump to different positions.
Handleris used to manage the update of theSeekBaras the song progresses. It runs tasks on the main thread at regular intervals.
SharedPreferencesallows saving user preferences, such as the last played song, loop settings, etc.
Create a layout file activity_player.xml in res/layout with the following XML code:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<TextView
android:id="@+id/song_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Song Title"
android:textSize="20sp"
android:textColor="@android:color/black"/>
<SeekBar
android:id="@+id/seekBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/song_title"
android:layout_marginTop="16dp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/seekBar"
android:layout_marginTop="16dp"
android:orientation="horizontal"
android:gravity="center">
<ImageButton
android:id="@+id/btn_previous"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_previous"
android:contentDescription="@string/previous_song"/>
<ImageButton
android:id="@+id/btn_play_pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_play"
android:contentDescription="@string/play_pause"/>
<ImageButton
android:id="@+id/btn_next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_next"
android:contentDescription="@string/next_song"/>
<ImageButton
android:id="@+id/btn_repeat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_repeat"
android:contentDescription="@string/repeat_song"/>
</LinearLayout>
</RelativeLayout>In your PlayerActivity.kt, initialize and handle the UI components, such as buttons and SeekBar.
To access audio files, you need to request the appropriate permission and use MediaStore to query available music files.
-
Request Permission: Update
AndroidManifest.xmlto include the permission.<uses-permission android:name="android.permission.READ_MEDIA_AUDIO"/>
-
Request Permission at Runtime: For Android 6.0 (API level 23) and above, request the permission at runtime in your
Activity.private fun requestPermissions() { if (checkSelfPermission(android.Manifest.permission.READ_MEDIA_AUDIO) != PackageManager.PERMISSION_GRANTED) { requestPermissions(arrayOf(android.Manifest.permission.READ_MEDIA_AUDIO), REQUEST_PERMISSION_CODE) } } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == REQUEST_PERMISSION_CODE) { if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // Permission granted, proceed with accessing music files } else { // Permission denied, handle accordingly } } }
Replace
REQUEST_PERMISSION_CODEwith a unique integer code. -
Query Music Files: Use
MediaStoreto get a list of audio files.private fun getMusicFiles(): List<String> { val musicList = mutableListOf<String>() val projection = arrayOf(MediaStore.Audio.Media.DATA) // Path to the audio file val cursor = contentResolver.query( MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection, null, null, null ) cursor?.use { val dataIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA) while (cursor.moveToNext()) { musicList.add(cursor.getString(dataIndex)) } } return musicList }
-
Create and Configure MediaPlayer: Initialize
MediaPlayerand set the data source.private fun initializeMediaPlayer(songPath: String) { mediaPlayer.apply { reset() try { setDataSource(songPath) prepare() start() } catch (e: Exception) { e.printStackTrace() Log.e("PlayerActivity", "Error initializing MediaPlayer: ${e.message}") } } }
-
Toggle Play/Pause: Change the playback state when the button is clicked.
private fun togglePlayPause() { if (mediaPlayer.isPlaying) { mediaPlayer.pause() playPauseButton.setImageResource(R.drawable.ic_play) // Change to play icon } else { mediaPlayer.start() playPauseButton.setImageResource(R.drawable.ic_pause) // Change to pause icon } }
-
Move Forward/Backward: Update the current position of the
MediaPlayer.private fun moveForward() { val currentPosition = mediaPlayer.currentPosition val newPosition = (currentPosition + 15000).coerceAtMost(mediaPlayer.duration) mediaPlayer.seekTo(newPosition) } private fun moveBackward() { val currentPosition = mediaPlayer.currentPosition val newPosition = (currentPosition - 15000).coerceAtLeast(0) mediaPlayer.seekTo(newPosition) }
Adjust the
15000milliseconds value to move forward or backward by different amounts.
-
Toggle Repeat: Change the repeat state and update the button icon.
private fun toggleRepeat() { isRepeating = !isRepeating repeatButton.setImageResource( if (isRepeating) R.drawable.ic_repeat_on else R.drawable.ic_repeat_off ) } private fun setupCompletionListener() { mediaPlayer.setOnCompletionListener { if (isRepeating) { mediaPlayer.start() // Replay the same song } else { playNextSong() // Play the next song } } }
-
Update and Handle SeekBar: Synchronize the
SeekBarwith the song's progress.private fun setupSeekBar() { seekBar.max = mediaPlayer.duration seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) { if (fromUser) { mediaPlayer.seekTo(progress) } } override fun onStartTrackingTouch(seekBar: SeekBar?) {} override fun onStopTrackingTouch(seekBar: SeekBar?) {} }) Timer().scheduleAtFixedRate(object : TimerTask() { override fun run() { runOnUiThread { seekBar.progress = mediaPlayer.currentPosition } } }, 0, 1000) }
This setup ensures that the SeekBar reflects the current position of the song and updates as the song plays.
Manage audio focus to ensure smooth playback when the app is in the foreground.
Use SharedPreferences to save settings like the last played song or repeat status.
Enhance the user interface with custom icons and styles for a better user experience.
This documentation provides a comprehensive guide to building a music player app using Kotlin in Android. Adjust and extend the features based on your specific requirements and user feedback.