├── .gitignore ├── .idea ├── .gitignore ├── .name ├── compiler.xml ├── deploymentTargetSelector.xml ├── gradle.xml ├── kotlinc.xml ├── migrations.xml ├── misc.xml └── other.xml ├── README.md ├── app ├── .gitignore ├── build.gradle.kts ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── harshRajpurohit │ │ └── musicPlayer │ │ ├── AboutActivity.kt │ │ ├── ApplicationClass.kt │ │ ├── FavouriteActivity.kt │ │ ├── FavouriteAdapter.kt │ │ ├── FeedbackActivity.kt │ │ ├── MainActivity.kt │ │ ├── Music.kt │ │ ├── MusicAdapter.kt │ │ ├── MusicService.kt │ │ ├── NotificationReceiver.kt │ │ ├── NowPlaying.kt │ │ ├── PlayNext.kt │ │ ├── PlayerActivity.kt │ │ ├── PlaylistActivity.kt │ │ ├── PlaylistDetails.kt │ │ ├── PlaylistViewAdapter.kt │ │ ├── SelectionActivity.kt │ │ └── SettingsActivity.kt │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ ├── about_icon.xml │ ├── add_icon.xml │ ├── back_icon.xml │ ├── booster_icon.xml │ ├── circle_icon.xml │ ├── custom_search_view.xml │ ├── delete_icon.xml │ ├── equalizer_icon.xml │ ├── exit_icon.xml │ ├── favourite_empty_icon.xml │ ├── favourite_icon.xml │ ├── feedback_icon.xml │ ├── gradient_black.xml │ ├── gradient_blue.xml │ ├── gradient_green.xml │ ├── gradient_pink.xml │ ├── gradient_purple.xml │ ├── ic_launcher_background.xml │ ├── ic_launcher_foreground.xml │ ├── music_icon.xml │ ├── music_player_icon_slash_screen.png │ ├── next_icon.xml │ ├── pause_icon.xml │ ├── play_icon.xml │ ├── play_next_icon.xml │ ├── playlist_icon.xml │ ├── previous_icon.xml │ ├── remove_icon.xml │ ├── repeat_icon.xml │ ├── scroll_bar_icon.xml │ ├── search_icon.xml │ ├── settings_icon.xml │ ├── share_icon.xml │ ├── shuffle_icon.xml │ ├── sort_icon.xml │ ├── splash_screen.xml │ ├── timer_icon.xml │ └── volume_icon.xml │ ├── layout │ ├── activity_about.xml │ ├── activity_favourite.xml │ ├── activity_feedback.xml │ ├── activity_main.xml │ ├── activity_play_next.xml │ ├── activity_player.xml │ ├── activity_playlist.xml │ ├── activity_playlist_details.xml │ ├── activity_selection.xml │ ├── activity_settings.xml │ ├── add_playlist_dialog.xml │ ├── audio_booster.xml │ ├── bottom_sheet_dialog.xml │ ├── details_view.xml │ ├── favourite_view.xml │ ├── fragment_now_playing.xml │ ├── more_features.xml │ ├── music_view.xml │ ├── nav_header.xml │ └── playlist_view.xml │ ├── menu │ ├── nav_menu.xml │ └── search_view_menu.xml │ ├── mipmap-anydpi-v26 │ ├── music_player_icon.xml │ └── music_player_icon_round.xml │ ├── mipmap-hdpi │ ├── music_player_icon.png │ ├── music_player_icon_foreground.png │ └── music_player_icon_round.png │ ├── mipmap-mdpi │ ├── music_player_icon.png │ ├── music_player_icon_foreground.png │ └── music_player_icon_round.png │ ├── mipmap-xhdpi │ ├── music_player_icon.png │ ├── music_player_icon_foreground.png │ └── music_player_icon_round.png │ ├── mipmap-xxhdpi │ ├── music_player_icon.png │ ├── music_player_icon_foreground.png │ └── music_player_icon_round.png │ ├── mipmap-xxxhdpi │ ├── music_player_icon.png │ ├── music_player_icon_foreground.png │ └── music_player_icon_round.png │ ├── values-night │ └── themes.xml │ ├── values │ ├── colors.xml │ ├── custom_styles.xml │ ├── music_player_icon_background.xml │ ├── strings.xml │ └── themes.xml │ └── xml │ ├── backup_rules.xml │ └── data_extraction_rules.xml ├── build.gradle.kts ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── music_player_screenshots ├── Music Player v2.0.1 04-07-2024.apk ├── screen_1.png ├── screen_2.png ├── screen_3.png ├── screen_4.png ├── screen_5.png ├── screen_6.png ├── screen_7.png └── screen_8.png └── settings.gradle.kts /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | Music Player -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/deploymentTargetSelector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | -------------------------------------------------------------------------------- /.idea/other.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 262 | 263 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MusicPlayer 2 | 3 | ![Status](https://img.shields.io/badge/Status-Active-brightgreen) ![Kotlin](https://img.shields.io/badge/Kotlin-100%25-brightgreen) 4 | 5 | **A Simple Yet Feature-Rich Music Player Application based on Native MediaPlayer.** 6 | 7 | --- 8 | 9 | ## 📦 APK Download 10 | 11 | Try it yourself and I’d love to hear your feedback 🙂: 12 | 13 | - [Download APK v2.0.0](https://drive.google.com/file/d/1HVmzkB4hSVR9_EcPAaj-VuQ4wbKfjsPj/view?usp=sharing) 14 | 15 | --- 16 | 17 | ## 📸 Screenshots 18 | 19 | **Actual app looks even better! 😃** 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | --- 33 | 34 | ## ✨ Features 35 | 36 | - **Play/Pause Audio** using earphones. 37 | - **Play Next** feature—schedule upcoming songs. 38 | - **Dark Theme** with black theme option in settings. 39 | - **Custom Color Gradient** for each song based on album art. 40 | - **Favorite Songs** for easy access. 41 | - **Create & Manage Playlists**. 42 | - **Sleep Timer** for auto-shutdown. 43 | - **Audio Booster** and **Built-in Equalizer**. 44 | - **Custom Themes** for personalization. 45 | - **Swipe to Refresh** UI. 46 | - **Background Playback** with custom notifications. 47 | - Android 10 **notification with seek bar**. 48 | - Clean and modern UI with **Material Design Widgets**. 49 | - **And much more...** 50 | 51 | --- 52 | 53 | ## 🎥 YouTube Course 54 | 55 | Watch the full YouTube playlist: 56 | [Watch on YouTube](https://youtube.com/playlist?list=PL8kbUJtS6hyaRM3LhIYOqIXK7LIoEJ4Qf) 57 | 58 | --- 59 | 60 | ## 💡 Note 61 | 62 | This project is much more improved and optimized than the YouTube course project, with new features and better performance. 63 | 64 | --- 65 | 66 | ## 💬 Feedback and Suggestions 67 | 68 | For any feedback or suggestions, feel free to contact me via email: 69 | 📧 [rajpurohitharsh2020@gmail.com](mailto:rajpurohitharsh2020@gmail.com) 70 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(libs.plugins.android.application) 3 | alias(libs.plugins.jetbrains.kotlin.android) 4 | } 5 | 6 | android { 7 | namespace = "com.harshRajpurohit.musicPlayer" 8 | compileSdk = 34 9 | 10 | defaultConfig { 11 | applicationId = "com.harshRajpurohit.musicPlayer" 12 | minSdk = 21 13 | targetSdk = 34 14 | versionCode = 11 15 | versionName = "2.0.1" 16 | 17 | // For showing build version name 18 | buildConfigField("String", "VERSION_NAME", "\"$versionName\"") 19 | } 20 | 21 | buildTypes { 22 | release { 23 | isMinifyEnabled = false 24 | proguardFiles( 25 | getDefaultProguardFile("proguard-android-optimize.txt"), 26 | "proguard-rules.pro" 27 | ) 28 | } 29 | } 30 | compileOptions { 31 | sourceCompatibility = JavaVersion.VERSION_1_8 32 | targetCompatibility = JavaVersion.VERSION_1_8 33 | } 34 | kotlinOptions { 35 | jvmTarget = "1.8" 36 | } 37 | 38 | buildFeatures{ 39 | // For viewBinding 40 | viewBinding = true 41 | 42 | // For showing build version name 43 | buildConfig = true 44 | } 45 | } 46 | 47 | dependencies { 48 | implementation(libs.androidx.core.ktx) 49 | implementation(libs.androidx.appcompat) 50 | implementation(libs.material) 51 | implementation(libs.androidx.activity) 52 | implementation(libs.androidx.constraintlayout) 53 | 54 | // Pull to Refresh 55 | implementation(libs.legacy.support) 56 | 57 | // Glide for image loading 58 | implementation(libs.glide) 59 | 60 | // For storing objects in shared preferences 61 | implementation(libs.gson) 62 | 63 | // Notification 64 | implementation(libs.androidx.media) 65 | 66 | // Vertical Seekbar 67 | implementation(libs.verticalseekbar) 68 | 69 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 28 | 29 | 32 | 33 | 38 | 39 | 44 | 45 | 50 | 51 | 56 | 57 | 62 | 63 | 68 | 69 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 86 | 87 | 88 | 89 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 109 | 110 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /app/src/main/java/com/harshRajpurohit/musicPlayer/AboutActivity.kt: -------------------------------------------------------------------------------- 1 | package com.harshRajpurohit.musicPlayer 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import com.harshRajpurohit.musicPlayer.databinding.ActivityAboutBinding 6 | 7 | class AboutActivity : AppCompatActivity() { 8 | 9 | lateinit var binding: ActivityAboutBinding 10 | 11 | override fun onCreate(savedInstanceState: Bundle?) { 12 | super.onCreate(savedInstanceState) 13 | setTheme(MainActivity.currentThemeNav[MainActivity.themeIndex]) 14 | binding = ActivityAboutBinding.inflate(layoutInflater) 15 | setContentView(binding.root) 16 | supportActionBar?.title = "About" 17 | binding.aboutText.text = aboutText() 18 | } 19 | private fun aboutText(): String{ 20 | return "Developed By: Harsh H. Rajpurohit" + 21 | "\n\nIf you want to provide feedback, I will love to hear that." 22 | } 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/harshRajpurohit/musicPlayer/ApplicationClass.kt: -------------------------------------------------------------------------------- 1 | package com.harshRajpurohit.musicPlayer 2 | 3 | import android.app.Application 4 | import android.app.NotificationChannel 5 | import android.app.NotificationManager 6 | import android.os.Build 7 | 8 | class ApplicationClass:Application() { 9 | companion object{ 10 | const val CHANNEL_ID = "MusicNotification" 11 | const val PLAY = "play" 12 | const val NEXT = "next" 13 | const val PREVIOUS = "previous" 14 | const val EXIT = "exit" 15 | } 16 | override fun onCreate() { 17 | super.onCreate() 18 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ 19 | val notificationChannel = NotificationChannel(CHANNEL_ID, "Now Playing Song", NotificationManager.IMPORTANCE_HIGH) 20 | notificationChannel.description = "Needed to Show Notification for Playing Song" 21 | //for lockscreen -> test this and let me know. 22 | // notificationChannel.importance = NotificationManager.IMPORTANCE_HIGH 23 | // notificationChannel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC 24 | 25 | val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager 26 | notificationManager.createNotificationChannel(notificationChannel) 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/java/com/harshRajpurohit/musicPlayer/FavouriteActivity.kt: -------------------------------------------------------------------------------- 1 | package com.harshRajpurohit.musicPlayer 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Intent 5 | import android.os.Bundle 6 | import android.view.View 7 | import androidx.appcompat.app.AppCompatActivity 8 | import androidx.recyclerview.widget.GridLayoutManager 9 | import com.harshRajpurohit.musicPlayer.databinding.ActivityFavouriteBinding 10 | 11 | class FavouriteActivity : AppCompatActivity() { 12 | 13 | private lateinit var binding: ActivityFavouriteBinding 14 | private lateinit var adapter: FavouriteAdapter 15 | 16 | companion object{ 17 | var favouriteSongs: ArrayList = ArrayList() 18 | var favouritesChanged: Boolean = false 19 | } 20 | 21 | override fun onCreate(savedInstanceState: Bundle?) { 22 | super.onCreate(savedInstanceState) 23 | setTheme(MainActivity.currentTheme[MainActivity.themeIndex]) 24 | binding = ActivityFavouriteBinding.inflate(layoutInflater) 25 | setContentView(binding.root) 26 | favouriteSongs = checkPlaylist(favouriteSongs) 27 | binding.backBtnFA.setOnClickListener { finish() } 28 | binding.favouriteRV.setHasFixedSize(true) 29 | binding.favouriteRV.setItemViewCacheSize(13) 30 | binding.favouriteRV.layoutManager = GridLayoutManager(this, 4) 31 | adapter = FavouriteAdapter(this, favouriteSongs) 32 | binding.favouriteRV.adapter = adapter 33 | 34 | favouritesChanged = false 35 | 36 | if(favouriteSongs.size < 1) binding.shuffleBtnFA.visibility = View.INVISIBLE 37 | 38 | if(favouriteSongs.isNotEmpty()) binding.instructionFV.visibility = View.GONE 39 | 40 | binding.shuffleBtnFA.setOnClickListener { 41 | val intent = Intent(this, PlayerActivity::class.java) 42 | intent.putExtra("index", 0) 43 | intent.putExtra("class", "FavouriteShuffle") 44 | startActivity(intent) 45 | } 46 | } 47 | 48 | @SuppressLint("NotifyDataSetChanged") 49 | override fun onResume() { 50 | super.onResume() 51 | if(favouritesChanged) { 52 | adapter.updateFavourites(favouriteSongs) 53 | favouritesChanged = false 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /app/src/main/java/com/harshRajpurohit/musicPlayer/FavouriteAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.harshRajpurohit.musicPlayer 2 | 3 | import android.annotation.SuppressLint 4 | import android.app.Activity 5 | import android.content.Context 6 | import android.content.Intent 7 | import android.graphics.drawable.ColorDrawable 8 | import android.view.LayoutInflater 9 | import android.view.ViewGroup 10 | import androidx.core.content.ContextCompat 11 | import androidx.recyclerview.widget.RecyclerView 12 | import com.bumptech.glide.Glide 13 | import com.bumptech.glide.request.RequestOptions 14 | import com.google.android.material.dialog.MaterialAlertDialogBuilder 15 | import com.google.android.material.snackbar.Snackbar 16 | import com.harshRajpurohit.musicPlayer.databinding.FavouriteViewBinding 17 | import com.harshRajpurohit.musicPlayer.databinding.MoreFeaturesBinding 18 | 19 | class FavouriteAdapter(private val context: Context, private var musicList: ArrayList,val playNext: Boolean = false) : RecyclerView.Adapter() { 20 | 21 | class MyHolder(binding: FavouriteViewBinding) : RecyclerView.ViewHolder(binding.root) { 22 | val image = binding.songImgFV 23 | val name = binding.songNameFV 24 | val root = binding.root 25 | } 26 | 27 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyHolder { 28 | return MyHolder(FavouriteViewBinding.inflate(LayoutInflater.from(context), parent, false)) 29 | } 30 | 31 | @SuppressLint("SetTextI18n") 32 | override fun onBindViewHolder(holder: MyHolder, position: Int) { 33 | holder.name.text = musicList[position].title 34 | Glide.with(context) 35 | .load(musicList[position].artUri) 36 | .apply(RequestOptions().placeholder(R.drawable.music_player_icon_slash_screen).centerCrop()) 37 | .into(holder.image) 38 | 39 | //when play next music is clicked 40 | if(playNext){ 41 | holder.root.setOnClickListener { 42 | val intent = Intent(context, PlayerActivity::class.java) 43 | intent.putExtra("index", position) 44 | intent.putExtra("class", "PlayNext") 45 | ContextCompat.startActivity(context, intent, null) 46 | } 47 | holder.root.setOnLongClickListener { 48 | val customDialog = LayoutInflater.from(context).inflate(R.layout.more_features, holder.root, false) 49 | val bindingMF = MoreFeaturesBinding.bind(customDialog) 50 | val dialog = MaterialAlertDialogBuilder(context).setView(customDialog) 51 | .create() 52 | dialog.show() 53 | dialog.window?.setBackgroundDrawable(ColorDrawable(0x99000000.toInt())) 54 | bindingMF.AddToPNBtn.text = "Remove" 55 | bindingMF.AddToPNBtn.setOnClickListener { 56 | if(position == PlayerActivity.songPosition) 57 | Snackbar.make((context as Activity).findViewById(R.id.linearLayoutPN), 58 | "Can't Remove Currently Playing Song.", Snackbar.LENGTH_SHORT).show() 59 | else{ 60 | if(PlayerActivity.songPosition < position && PlayerActivity.songPosition != 0) --PlayerActivity.songPosition 61 | PlayNext.playNextList.removeAt(position) 62 | PlayerActivity.musicListPA.removeAt(position) 63 | notifyItemRemoved(position) 64 | } 65 | dialog.dismiss() 66 | } 67 | return@setOnLongClickListener true 68 | } 69 | }else{ 70 | holder.root.setOnClickListener { 71 | val intent = Intent(context, PlayerActivity::class.java) 72 | intent.putExtra("index", position) 73 | intent.putExtra("class", "FavouriteAdapter") 74 | ContextCompat.startActivity(context, intent, null) 75 | } 76 | } 77 | } 78 | 79 | override fun getItemCount(): Int { 80 | return musicList.size 81 | } 82 | 83 | @SuppressLint("NotifyDataSetChanged") 84 | fun updateFavourites(newList: ArrayList){ 85 | musicList = ArrayList() 86 | musicList.addAll(newList) 87 | notifyDataSetChanged() 88 | } 89 | 90 | } -------------------------------------------------------------------------------- /app/src/main/java/com/harshRajpurohit/musicPlayer/FeedbackActivity.kt: -------------------------------------------------------------------------------- 1 | package com.harshRajpurohit.musicPlayer 2 | 3 | //Not Used Now 4 | 5 | //import android.content.Context 6 | //import android.net.ConnectivityManager 7 | //import android.os.Bundle 8 | //import android.widget.Toast 9 | //import androidx.appcompat.app.AppCompatActivity 10 | //import com.harshRajpurohit.musicPlayer.databinding.ActivityFeedbackBinding 11 | //import java.util.* 12 | //import javax.mail.* 13 | //import javax.mail.internet.InternetAddress 14 | //import javax.mail.internet.MimeMessage 15 | // 16 | //class FeedbackActivity : AppCompatActivity() { 17 | // 18 | // lateinit var binding: ActivityFeedbackBinding 19 | // 20 | // override fun onCreate(savedInstanceState: Bundle?) { 21 | // super.onCreate(savedInstanceState) 22 | // setTheme(MainActivity.currentThemeNav[MainActivity.themeIndex]) 23 | // binding = ActivityFeedbackBinding.inflate(layoutInflater) 24 | // setContentView(binding.root) 25 | // supportActionBar?.title = "Feedback" 26 | // binding.sendFA.setOnClickListener { 27 | // val feedbackMsg = binding.feedbackMsgFA.text.toString() + "\n" + binding.emailFA.text.toString() 28 | // val subject = binding.topicFA.text.toString() 29 | // val userName = "musicworldapp2021@gmail.com" 30 | // val pass = "musicWorldApp2021" 31 | // val cm = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager 32 | // if(feedbackMsg.isNotEmpty() && subject.isNotEmpty() && (cm.activeNetworkInfo?.isConnectedOrConnecting == true)){ 33 | // Thread{ 34 | // try { 35 | // val properties = Properties() 36 | // properties["mail.smtp.auth"] = "true" 37 | // properties["mail.smtp.starttls.enable"] = "true" 38 | // properties["mail.smtp.host"] = "smtp.gmail.com" 39 | // properties["mail.smtp.port"] = "587" 40 | // val session = Session.getInstance(properties, object : Authenticator(){ 41 | // override fun getPasswordAuthentication(): PasswordAuthentication { 42 | // return PasswordAuthentication(userName, pass) 43 | // } 44 | // }) 45 | // val mail = MimeMessage(session) 46 | // mail.subject = subject 47 | // mail.setText(feedbackMsg) 48 | // mail.setFrom(InternetAddress(userName)) 49 | // mail.setRecipients(Message.RecipientType.TO, InternetAddress.parse(userName)) 50 | // Transport.send(mail) 51 | // }catch (e: Exception){Toast.makeText(this, e.toString(), Toast.LENGTH_SHORT).show()} 52 | // }.start() 53 | // Toast.makeText(this, "Thanks For Feedback!!", Toast.LENGTH_SHORT).show() 54 | // finish() 55 | // } 56 | // else Toast.makeText(this, "Went Something Wrong!!", Toast.LENGTH_SHORT).show() 57 | // } 58 | // } 59 | //} -------------------------------------------------------------------------------- /app/src/main/java/com/harshRajpurohit/musicPlayer/Music.kt: -------------------------------------------------------------------------------- 1 | package com.harshRajpurohit.musicPlayer 2 | 3 | import android.content.Context 4 | import android.graphics.Bitmap 5 | import android.graphics.Color 6 | import android.media.MediaMetadataRetriever 7 | import androidx.appcompat.app.AlertDialog 8 | import com.google.android.material.color.MaterialColors 9 | import java.io.File 10 | import java.util.concurrent.TimeUnit 11 | import kotlin.system.exitProcess 12 | 13 | 14 | data class Music( 15 | val id: String, 16 | val title: String, 17 | val album: String, 18 | val artist: String, 19 | val duration: Long = 0, 20 | val path: String, 21 | val artUri: String 22 | ) 23 | 24 | class Playlist { 25 | lateinit var name: String 26 | lateinit var playlist: ArrayList 27 | lateinit var createdBy: String 28 | lateinit var createdOn: String 29 | } 30 | 31 | class MusicPlaylist { 32 | var ref: ArrayList = ArrayList() 33 | } 34 | 35 | fun formatDuration(duration: Long): String { 36 | val minutes = TimeUnit.MINUTES.convert(duration, TimeUnit.MILLISECONDS) 37 | val seconds = (TimeUnit.SECONDS.convert(duration, TimeUnit.MILLISECONDS) - 38 | minutes * TimeUnit.SECONDS.convert(1, TimeUnit.MINUTES)) 39 | return String.format("%02d:%02d", minutes, seconds) 40 | } 41 | 42 | fun getImgArt(path: String): ByteArray? { 43 | val retriever = MediaMetadataRetriever() 44 | retriever.setDataSource(path) 45 | return retriever.embeddedPicture 46 | } 47 | 48 | fun setSongPosition(increment: Boolean) { 49 | if (!PlayerActivity.repeat) { 50 | if (increment) { 51 | if (PlayerActivity.musicListPA.size - 1 == PlayerActivity.songPosition) 52 | PlayerActivity.songPosition = 0 53 | else ++PlayerActivity.songPosition 54 | } else { 55 | if (0 == PlayerActivity.songPosition) 56 | PlayerActivity.songPosition = PlayerActivity.musicListPA.size - 1 57 | else --PlayerActivity.songPosition 58 | } 59 | } 60 | } 61 | 62 | fun exitApplication() { 63 | if (PlayerActivity.musicService != null) { 64 | PlayerActivity.musicService!!.audioManager.abandonAudioFocus(PlayerActivity.musicService) 65 | PlayerActivity.musicService!!.stopForeground(true) 66 | PlayerActivity.musicService!!.mediaPlayer!!.release() 67 | PlayerActivity.musicService = null 68 | } 69 | exitProcess(1) 70 | } 71 | 72 | fun favouriteChecker(id: String): Int { 73 | PlayerActivity.isFavourite = false 74 | FavouriteActivity.favouriteSongs.forEachIndexed { index, music -> 75 | if (id == music.id) { 76 | PlayerActivity.isFavourite = true 77 | return index 78 | } 79 | } 80 | return -1 81 | } 82 | 83 | fun checkPlaylist(playlist: ArrayList): ArrayList { 84 | val indicesToRemove = mutableListOf() 85 | 86 | playlist.forEachIndexed { index, music -> 87 | if (!File(music.path).exists()) indicesToRemove.add(index) 88 | } 89 | 90 | indicesToRemove.sortDescending() 91 | indicesToRemove.forEach { index -> playlist.removeAt(index) } 92 | return playlist 93 | } 94 | 95 | fun setDialogBtnBackground(context: Context, dialog: AlertDialog) { 96 | //setting button text 97 | dialog.getButton(android.app.AlertDialog.BUTTON_POSITIVE)?.setTextColor( 98 | MaterialColors.getColor(context, R.attr.dialogTextColor, Color.WHITE) 99 | ) 100 | dialog.getButton(android.app.AlertDialog.BUTTON_NEGATIVE)?.setTextColor( 101 | MaterialColors.getColor(context, R.attr.dialogTextColor, Color.WHITE) 102 | ) 103 | 104 | //setting button background 105 | dialog.getButton(android.app.AlertDialog.BUTTON_POSITIVE)?.setBackgroundColor( 106 | MaterialColors.getColor(context, R.attr.dialogBtnBackground, Color.RED) 107 | ) 108 | dialog.getButton(android.app.AlertDialog.BUTTON_NEGATIVE)?.setBackgroundColor( 109 | MaterialColors.getColor(context, R.attr.dialogBtnBackground, Color.RED) 110 | ) 111 | } 112 | 113 | fun getMainColor(img: Bitmap): Int { 114 | val newImg = Bitmap.createScaledBitmap(img, 1, 1, true) 115 | val color = newImg.getPixel(0, 0) 116 | newImg.recycle() 117 | return color 118 | } -------------------------------------------------------------------------------- /app/src/main/java/com/harshRajpurohit/musicPlayer/MusicAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.harshRajpurohit.musicPlayer 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import android.graphics.Color 6 | import android.graphics.drawable.ColorDrawable 7 | import android.text.SpannableStringBuilder 8 | import android.text.format.DateUtils 9 | import android.view.LayoutInflater 10 | import android.view.ViewGroup 11 | import androidx.appcompat.app.AlertDialog 12 | import androidx.core.content.ContextCompat 13 | import androidx.core.text.bold 14 | import androidx.recyclerview.widget.RecyclerView 15 | import com.bumptech.glide.Glide 16 | import com.bumptech.glide.request.RequestOptions 17 | import com.google.android.material.dialog.MaterialAlertDialogBuilder 18 | import com.google.android.material.snackbar.Snackbar 19 | import com.harshRajpurohit.musicPlayer.MusicAdapter.MyHolder 20 | import com.harshRajpurohit.musicPlayer.databinding.DetailsViewBinding 21 | import com.harshRajpurohit.musicPlayer.databinding.MoreFeaturesBinding 22 | import com.harshRajpurohit.musicPlayer.databinding.MusicViewBinding 23 | 24 | class MusicAdapter(private val context: Context, private var musicList: ArrayList, private val playlistDetails: Boolean = false, 25 | private val selectionActivity: Boolean = false) 26 | : RecyclerView.Adapter() { 27 | 28 | class MyHolder(binding: MusicViewBinding) : RecyclerView.ViewHolder(binding.root) { 29 | val title = binding.songNameMV 30 | val album = binding.songAlbumMV 31 | val image = binding.imageMV 32 | val duration = binding.songDuration 33 | val root = binding.root 34 | } 35 | 36 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyHolder { 37 | return MyHolder(MusicViewBinding.inflate(LayoutInflater.from(context), parent, false)) 38 | } 39 | 40 | override fun onBindViewHolder(holder: MyHolder, position: Int) { 41 | holder.title.text = musicList[position].title 42 | holder.album.text = musicList[position].album 43 | holder.duration.text = formatDuration(musicList[position].duration) 44 | Glide.with(context) 45 | .load(musicList[position].artUri) 46 | .apply(RequestOptions().placeholder(R.drawable.music_player_icon_slash_screen).centerCrop()) 47 | .into(holder.image) 48 | 49 | //for play next feature 50 | if(!selectionActivity) 51 | holder.root.setOnLongClickListener { 52 | val customDialog = LayoutInflater.from(context).inflate(R.layout.more_features, holder.root, false) 53 | val bindingMF = MoreFeaturesBinding.bind(customDialog) 54 | val dialog = MaterialAlertDialogBuilder(context).setView(customDialog) 55 | .create() 56 | dialog.show() 57 | dialog.window?.setBackgroundDrawable(ColorDrawable(0x99000000.toInt())) 58 | 59 | bindingMF.AddToPNBtn.setOnClickListener { 60 | try { 61 | if(PlayNext.playNextList.isEmpty()){ 62 | PlayNext.playNextList.add(PlayerActivity.musicListPA[PlayerActivity.songPosition]) 63 | PlayerActivity.songPosition = 0 64 | } 65 | 66 | PlayNext.playNextList.add(musicList[position]) 67 | PlayerActivity.musicListPA = ArrayList() 68 | PlayerActivity.musicListPA.addAll(PlayNext.playNextList) 69 | }catch (e: Exception){ 70 | Snackbar.make(context, holder.root,"Play A Song First!!", 3000).show() 71 | } 72 | dialog.dismiss() 73 | } 74 | 75 | bindingMF.infoBtn.setOnClickListener { 76 | dialog.dismiss() 77 | val detailsDialog = LayoutInflater.from(context).inflate(R.layout.details_view, bindingMF.root, false) 78 | val binder = DetailsViewBinding.bind(detailsDialog) 79 | binder.detailsTV.setTextColor(Color.WHITE) 80 | binder.root.setBackgroundColor(Color.TRANSPARENT) 81 | val dDialog = MaterialAlertDialogBuilder(context) 82 | // .setBackground(ColorDrawable(0x99000000.toInt())) 83 | .setView(detailsDialog) 84 | .setPositiveButton("OK"){self, _ -> self.dismiss()} 85 | .setCancelable(false) 86 | .create() 87 | dDialog.show() 88 | dDialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(Color.RED) 89 | setDialogBtnBackground(context, dDialog) 90 | dDialog.window?.setBackgroundDrawable(ColorDrawable(0x99000000.toInt())) 91 | val str = SpannableStringBuilder().bold { append("DETAILS\n\nName: ") } 92 | .append(musicList[position].title) 93 | .bold { append("\n\nDuration: ") }.append(DateUtils.formatElapsedTime(musicList[position].duration/1000)) 94 | .bold { append("\n\nLocation: ") }.append(musicList[position].path) 95 | binder.detailsTV.text = str 96 | } 97 | 98 | return@setOnLongClickListener true 99 | } 100 | 101 | when{ 102 | playlistDetails ->{ 103 | holder.root.setOnClickListener { 104 | sendIntent(ref = "PlaylistDetailsAdapter", pos = position) 105 | } 106 | } 107 | selectionActivity ->{ 108 | holder.root.setOnClickListener { 109 | if(addSong(musicList[position])) 110 | holder.root.setBackgroundColor(ContextCompat.getColor(context, R.color.cool_pink)) 111 | else 112 | holder.root.setBackgroundColor(ContextCompat.getColor(context, R.color.white)) 113 | 114 | } 115 | } 116 | else ->{ 117 | holder.root.setOnClickListener { 118 | when{ 119 | MainActivity.search -> sendIntent(ref = "MusicAdapterSearch", pos = position) 120 | musicList[position].id == PlayerActivity.nowPlayingId -> 121 | sendIntent(ref = "NowPlaying", pos = PlayerActivity.songPosition) 122 | else->sendIntent(ref="MusicAdapter", pos = position) } } 123 | } 124 | 125 | } 126 | } 127 | 128 | override fun getItemCount(): Int { 129 | return musicList.size 130 | } 131 | 132 | fun updateMusicList(searchList : ArrayList){ 133 | musicList = ArrayList() 134 | musicList.addAll(searchList) 135 | notifyDataSetChanged() 136 | } 137 | private fun sendIntent(ref: String, pos: Int){ 138 | val intent = Intent(context, PlayerActivity::class.java) 139 | intent.putExtra("index", pos) 140 | intent.putExtra("class", ref) 141 | ContextCompat.startActivity(context, intent, null) 142 | } 143 | private fun addSong(song: Music): Boolean{ 144 | PlaylistActivity.musicPlaylist.ref[PlaylistDetails.currentPlaylistPos].playlist.forEachIndexed { index, music -> 145 | if(song.id == music.id){ 146 | PlaylistActivity.musicPlaylist.ref[PlaylistDetails.currentPlaylistPos].playlist.removeAt(index) 147 | return false 148 | } 149 | } 150 | PlaylistActivity.musicPlaylist.ref[PlaylistDetails.currentPlaylistPos].playlist.add(song) 151 | return true 152 | } 153 | fun refreshPlaylist(){ 154 | musicList = ArrayList() 155 | musicList = PlaylistActivity.musicPlaylist.ref[PlaylistDetails.currentPlaylistPos].playlist 156 | notifyDataSetChanged() 157 | } 158 | } -------------------------------------------------------------------------------- /app/src/main/java/com/harshRajpurohit/musicPlayer/MusicService.kt: -------------------------------------------------------------------------------- 1 | package com.harshRajpurohit.musicPlayer 2 | 3 | import android.annotation.SuppressLint 4 | import android.app.PendingIntent 5 | import android.app.Service 6 | import android.content.Context 7 | import android.content.Intent 8 | import android.graphics.BitmapFactory 9 | import android.media.AudioManager 10 | import android.media.MediaPlayer 11 | import android.media.audiofx.LoudnessEnhancer 12 | import android.os.* 13 | import android.support.v4.media.MediaMetadataCompat 14 | import android.support.v4.media.session.MediaSessionCompat 15 | import android.support.v4.media.session.PlaybackStateCompat 16 | import com.bumptech.glide.Glide 17 | import com.bumptech.glide.request.RequestOptions 18 | 19 | class MusicService : Service(), AudioManager.OnAudioFocusChangeListener { 20 | private var myBinder = MyBinder() 21 | var mediaPlayer: MediaPlayer? = null 22 | private lateinit var mediaSession: MediaSessionCompat 23 | private lateinit var runnable: Runnable 24 | lateinit var audioManager: AudioManager 25 | 26 | override fun onBind(intent: Intent?): IBinder { 27 | mediaSession = MediaSessionCompat(baseContext, "My Music") 28 | return myBinder 29 | } 30 | 31 | inner class MyBinder : Binder() { 32 | fun currentService(): MusicService { 33 | return this@MusicService 34 | } 35 | } 36 | 37 | @SuppressLint("UnspecifiedImmutableFlag") 38 | fun showNotification(playPauseBtn: Int) { 39 | val intent = Intent(baseContext, MainActivity::class.java) 40 | 41 | val flag = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { 42 | PendingIntent.FLAG_IMMUTABLE 43 | } else { 44 | PendingIntent.FLAG_UPDATE_CURRENT 45 | } 46 | 47 | val contentIntent = PendingIntent.getActivity(this, 0, intent, flag) 48 | 49 | val prevIntent = Intent( 50 | baseContext, NotificationReceiver::class.java 51 | ).setAction(ApplicationClass.PREVIOUS) 52 | val prevPendingIntent = PendingIntent.getBroadcast(baseContext, 0, prevIntent, flag) 53 | 54 | val playIntent = 55 | Intent(baseContext, NotificationReceiver::class.java).setAction(ApplicationClass.PLAY) 56 | val playPendingIntent = PendingIntent.getBroadcast(baseContext, 0, playIntent, flag) 57 | 58 | val nextIntent = 59 | Intent(baseContext, NotificationReceiver::class.java).setAction(ApplicationClass.NEXT) 60 | val nextPendingIntent = PendingIntent.getBroadcast(baseContext, 0, nextIntent, flag) 61 | 62 | val exitIntent = 63 | Intent(baseContext, NotificationReceiver::class.java).setAction(ApplicationClass.EXIT) 64 | val exitPendingIntent = PendingIntent.getBroadcast(baseContext, 0, exitIntent, flag) 65 | 66 | val imgArt = getImgArt(PlayerActivity.musicListPA[PlayerActivity.songPosition].path) 67 | val image = if (imgArt != null) { 68 | BitmapFactory.decodeByteArray(imgArt, 0, imgArt.size) 69 | } else { 70 | BitmapFactory.decodeResource(resources, R.drawable.music_player_icon_slash_screen) 71 | } 72 | 73 | val notification = 74 | androidx.core.app.NotificationCompat.Builder(baseContext, ApplicationClass.CHANNEL_ID) 75 | .setContentIntent(contentIntent) 76 | .setContentTitle(PlayerActivity.musicListPA[PlayerActivity.songPosition].title) 77 | .setContentText(PlayerActivity.musicListPA[PlayerActivity.songPosition].artist) 78 | .setSmallIcon(R.drawable.music_icon).setLargeIcon(image) 79 | .setStyle(androidx.media.app.NotificationCompat.MediaStyle().setMediaSession(mediaSession.sessionToken)) 80 | .setPriority(androidx.core.app.NotificationCompat.PRIORITY_HIGH) 81 | .setVisibility(androidx.core.app.NotificationCompat.VISIBILITY_PUBLIC) 82 | .setOnlyAlertOnce(true) 83 | .addAction(R.drawable.previous_icon, "Previous", prevPendingIntent) 84 | .addAction(playPauseBtn, "Play", playPendingIntent) 85 | .addAction(R.drawable.next_icon, "Next", nextPendingIntent) 86 | .addAction(R.drawable.exit_icon, "Exit", exitPendingIntent) 87 | .build() 88 | 89 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { 90 | 91 | mediaSession.setMetadata( 92 | MediaMetadataCompat.Builder().putLong( 93 | MediaMetadataCompat.METADATA_KEY_DURATION, mediaPlayer!!.duration.toLong() 94 | ).build() 95 | ) 96 | 97 | mediaSession.setPlaybackState(getPlayBackState()) 98 | mediaSession.setCallback(object : MediaSessionCompat.Callback() { 99 | 100 | //called when play button is pressed 101 | override fun onPlay() { 102 | super.onPlay() 103 | handlePlayPause() 104 | } 105 | 106 | //called when pause button is pressed 107 | override fun onPause() { 108 | super.onPause() 109 | handlePlayPause() 110 | } 111 | 112 | //called when next button is pressed 113 | override fun onSkipToNext() { 114 | super.onSkipToNext() 115 | prevNextSong(increment = true, context = baseContext) 116 | } 117 | 118 | //called when previous button is pressed 119 | override fun onSkipToPrevious() { 120 | super.onSkipToPrevious() 121 | prevNextSong(increment = false, context = baseContext) 122 | } 123 | 124 | //called when headphones buttons are pressed 125 | //currently only pause or play music on button click 126 | override fun onMediaButtonEvent(mediaButtonEvent: Intent?): Boolean { 127 | handlePlayPause() 128 | return super.onMediaButtonEvent(mediaButtonEvent) 129 | } 130 | 131 | //called when seekbar is changed 132 | override fun onSeekTo(pos: Long) { 133 | super.onSeekTo(pos) 134 | mediaPlayer?.seekTo(pos.toInt()) 135 | 136 | mediaSession.setPlaybackState(getPlayBackState()) 137 | } 138 | }) 139 | } 140 | 141 | startForeground(13, notification) 142 | } 143 | 144 | fun createMediaPlayer() { 145 | try { 146 | if (mediaPlayer == null) mediaPlayer = MediaPlayer() 147 | mediaPlayer?.reset() 148 | mediaPlayer?.setDataSource(PlayerActivity.musicListPA[PlayerActivity.songPosition].path) 149 | mediaPlayer?.prepare() 150 | 151 | PlayerActivity.binding.playPauseBtnPA.setIconResource(R.drawable.pause_icon) 152 | showNotification(R.drawable.pause_icon) 153 | PlayerActivity.binding.tvSeekBarStart.text = 154 | formatDuration(mediaPlayer!!.currentPosition.toLong()) 155 | PlayerActivity.binding.tvSeekBarEnd.text = 156 | formatDuration(mediaPlayer!!.duration.toLong()) 157 | PlayerActivity.binding.seekBarPA.progress = 0 158 | PlayerActivity.binding.seekBarPA.max = mediaPlayer!!.duration 159 | PlayerActivity.nowPlayingId = PlayerActivity.musicListPA[PlayerActivity.songPosition].id 160 | PlayerActivity.loudnessEnhancer = LoudnessEnhancer(mediaPlayer!!.audioSessionId) 161 | PlayerActivity.loudnessEnhancer.enabled = true 162 | } catch (e: Exception) { 163 | return 164 | } 165 | } 166 | 167 | fun seekBarSetup() { 168 | runnable = Runnable { 169 | PlayerActivity.binding.tvSeekBarStart.text = 170 | formatDuration(mediaPlayer!!.currentPosition.toLong()) 171 | PlayerActivity.binding.seekBarPA.progress = mediaPlayer!!.currentPosition 172 | Handler(Looper.getMainLooper()).postDelayed(runnable, 200) 173 | } 174 | Handler(Looper.getMainLooper()).postDelayed(runnable, 0) 175 | } 176 | 177 | fun getPlayBackState(): PlaybackStateCompat { 178 | val playbackSpeed = if (PlayerActivity.isPlaying) 1F else 0F 179 | 180 | return PlaybackStateCompat.Builder().setState( 181 | if (mediaPlayer?.isPlaying == true) PlaybackStateCompat.STATE_PLAYING else PlaybackStateCompat.STATE_PAUSED, 182 | mediaPlayer!!.currentPosition.toLong(), playbackSpeed) 183 | .setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE or PlaybackStateCompat.ACTION_SEEK_TO or PlaybackStateCompat.ACTION_SKIP_TO_NEXT or PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS) 184 | .build() 185 | } 186 | 187 | fun handlePlayPause() { 188 | if (PlayerActivity.isPlaying) pauseMusic() 189 | else playMusic() 190 | 191 | //update playback state for notification 192 | mediaSession.setPlaybackState(getPlayBackState()) 193 | } 194 | 195 | 196 | 197 | private fun prevNextSong(increment: Boolean, context: Context){ 198 | 199 | setSongPosition(increment = increment) 200 | 201 | PlayerActivity.musicService?.createMediaPlayer() 202 | Glide.with(context) 203 | .load(PlayerActivity.musicListPA[PlayerActivity.songPosition].artUri) 204 | .apply(RequestOptions().placeholder(R.drawable.music_player_icon_slash_screen).centerCrop()) 205 | .into(PlayerActivity.binding.songImgPA) 206 | 207 | PlayerActivity.binding.songNamePA.text = PlayerActivity.musicListPA[PlayerActivity.songPosition].title 208 | 209 | Glide.with(context) 210 | .load(PlayerActivity.musicListPA[PlayerActivity.songPosition].artUri) 211 | .apply(RequestOptions().placeholder(R.drawable.music_player_icon_slash_screen).centerCrop()) 212 | .into(NowPlaying.binding.songImgNP) 213 | 214 | NowPlaying.binding.songNameNP.text = PlayerActivity.musicListPA[PlayerActivity.songPosition].title 215 | 216 | playMusic() 217 | 218 | PlayerActivity.fIndex = favouriteChecker(PlayerActivity.musicListPA[PlayerActivity.songPosition].id) 219 | if(PlayerActivity.isFavourite) PlayerActivity.binding.favouriteBtnPA.setImageResource(R.drawable.favourite_icon) 220 | else PlayerActivity.binding.favouriteBtnPA.setImageResource(R.drawable.favourite_empty_icon) 221 | 222 | //update playback state for notification 223 | mediaSession.setPlaybackState(getPlayBackState()) 224 | } 225 | 226 | override fun onAudioFocusChange(focusChange: Int) { 227 | if (focusChange <= 0) { 228 | pauseMusic() 229 | } 230 | // else{ 231 | // playMusic() 232 | // } 233 | } 234 | 235 | private fun playMusic(){ 236 | //play music 237 | PlayerActivity.binding.playPauseBtnPA.setIconResource(R.drawable.pause_icon) 238 | NowPlaying.binding.playPauseBtnNP.setIconResource(R.drawable.pause_icon) 239 | PlayerActivity.isPlaying = true 240 | mediaPlayer?.start() 241 | showNotification(R.drawable.pause_icon) 242 | } 243 | 244 | private fun pauseMusic(){ 245 | //pause music 246 | PlayerActivity.binding.playPauseBtnPA.setIconResource(R.drawable.play_icon) 247 | NowPlaying.binding.playPauseBtnNP.setIconResource(R.drawable.play_icon) 248 | PlayerActivity.isPlaying = false 249 | mediaPlayer!!.pause() 250 | showNotification(R.drawable.play_icon) 251 | } 252 | 253 | 254 | 255 | 256 | //for making persistent 257 | override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { 258 | return START_STICKY 259 | } 260 | 261 | } -------------------------------------------------------------------------------- /app/src/main/java/com/harshRajpurohit/musicPlayer/NotificationReceiver.kt: -------------------------------------------------------------------------------- 1 | package com.harshRajpurohit.musicPlayer 2 | 3 | import android.content.BroadcastReceiver 4 | import android.content.Context 5 | import android.content.Intent 6 | import com.bumptech.glide.Glide 7 | import com.bumptech.glide.request.RequestOptions 8 | 9 | class NotificationReceiver:BroadcastReceiver() { 10 | override fun onReceive(context: Context?, intent: Intent?) { 11 | when(intent?.action){ 12 | //only play next or prev song, when music list contains more than one song 13 | ApplicationClass.PREVIOUS -> if(PlayerActivity.musicListPA.size > 1) prevNextSong(increment = false, context = context!!) 14 | ApplicationClass.PLAY -> if(PlayerActivity.isPlaying) pauseMusic() else playMusic() 15 | ApplicationClass.NEXT -> if(PlayerActivity.musicListPA.size > 1) prevNextSong(increment = true, context = context!!) 16 | ApplicationClass.EXIT ->{ 17 | exitApplication() 18 | } 19 | } 20 | } 21 | private fun playMusic(){ 22 | PlayerActivity.isPlaying = true 23 | PlayerActivity.musicService!!.mediaPlayer!!.start() 24 | PlayerActivity.musicService!!.showNotification(R.drawable.pause_icon) 25 | PlayerActivity.binding.playPauseBtnPA.setIconResource(R.drawable.pause_icon) 26 | //for handling app crash during notification play - pause btn (While app opened through intent) 27 | try{ NowPlaying.binding.playPauseBtnNP.setIconResource(R.drawable.pause_icon) }catch (_: Exception){} 28 | } 29 | 30 | private fun pauseMusic(){ 31 | PlayerActivity.isPlaying = false 32 | PlayerActivity.musicService!!.mediaPlayer!!.pause() 33 | PlayerActivity.musicService!!.showNotification(R.drawable.play_icon) 34 | PlayerActivity.binding.playPauseBtnPA.setIconResource(R.drawable.play_icon) 35 | //for handling app crash during notification play - pause btn (While app opened through intent) 36 | try{ NowPlaying.binding.playPauseBtnNP.setIconResource(R.drawable.play_icon) }catch (_: Exception){} 37 | } 38 | 39 | private fun prevNextSong(increment: Boolean, context: Context){ 40 | setSongPosition(increment = increment) 41 | PlayerActivity.musicService!!.createMediaPlayer() 42 | Glide.with(context) 43 | .load(PlayerActivity.musicListPA[PlayerActivity.songPosition].artUri) 44 | .apply(RequestOptions().placeholder(R.drawable.music_player_icon_slash_screen).centerCrop()) 45 | .into(PlayerActivity.binding.songImgPA) 46 | PlayerActivity.binding.songNamePA.text = PlayerActivity.musicListPA[PlayerActivity.songPosition].title 47 | Glide.with(context) 48 | .load(PlayerActivity.musicListPA[PlayerActivity.songPosition].artUri) 49 | .apply(RequestOptions().placeholder(R.drawable.music_player_icon_slash_screen).centerCrop()) 50 | .into(NowPlaying.binding.songImgNP) 51 | NowPlaying.binding.songNameNP.text = PlayerActivity.musicListPA[PlayerActivity.songPosition].title 52 | playMusic() 53 | PlayerActivity.fIndex = favouriteChecker(PlayerActivity.musicListPA[PlayerActivity.songPosition].id) 54 | if(PlayerActivity.isFavourite) PlayerActivity.binding.favouriteBtnPA.setImageResource(R.drawable.favourite_icon) 55 | else PlayerActivity.binding.favouriteBtnPA.setImageResource(R.drawable.favourite_empty_icon) 56 | } 57 | } -------------------------------------------------------------------------------- /app/src/main/java/com/harshRajpurohit/musicPlayer/NowPlaying.kt: -------------------------------------------------------------------------------- 1 | package com.harshRajpurohit.musicPlayer 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Intent 5 | import android.os.Bundle 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import androidx.core.content.ContextCompat 10 | import androidx.fragment.app.Fragment 11 | import com.bumptech.glide.Glide 12 | import com.bumptech.glide.request.RequestOptions 13 | import com.harshRajpurohit.musicPlayer.databinding.FragmentNowPlayingBinding 14 | 15 | class NowPlaying : Fragment() { 16 | 17 | companion object{ 18 | @SuppressLint("StaticFieldLeak") 19 | lateinit var binding: FragmentNowPlayingBinding 20 | } 21 | 22 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { 23 | requireContext().theme.applyStyle(MainActivity.currentTheme[MainActivity.themeIndex], true) 24 | val view = inflater.inflate(R.layout.fragment_now_playing, container, false) 25 | binding = FragmentNowPlayingBinding.bind(view) 26 | binding.root.visibility = View.INVISIBLE 27 | binding.playPauseBtnNP.setOnClickListener { 28 | if(PlayerActivity.isPlaying) pauseMusic() else playMusic() 29 | } 30 | binding.nextBtnNP.setOnClickListener { 31 | setSongPosition(increment = true) 32 | PlayerActivity.musicService!!.createMediaPlayer() 33 | Glide.with(requireContext()) 34 | .load(PlayerActivity.musicListPA[PlayerActivity.songPosition].artUri) 35 | .apply(RequestOptions().placeholder(R.drawable.music_player_icon_slash_screen).centerCrop()) 36 | .into(binding.songImgNP) 37 | binding.songNameNP.text = PlayerActivity.musicListPA[PlayerActivity.songPosition].title 38 | PlayerActivity.musicService!!.showNotification(R.drawable.pause_icon) 39 | playMusic() 40 | } 41 | binding.root.setOnClickListener { 42 | val intent = Intent(requireContext(), PlayerActivity::class.java) 43 | intent.putExtra("index", PlayerActivity.songPosition) 44 | intent.putExtra("class", "NowPlaying") 45 | ContextCompat.startActivity(requireContext(), intent, null) 46 | } 47 | return view 48 | } 49 | 50 | override fun onResume() { 51 | super.onResume() 52 | if(PlayerActivity.musicService != null){ 53 | binding.root.visibility = View.VISIBLE 54 | binding.songNameNP.isSelected = true 55 | Glide.with(requireContext()) 56 | .load(PlayerActivity.musicListPA[PlayerActivity.songPosition].artUri) 57 | .apply(RequestOptions().placeholder(R.drawable.music_player_icon_slash_screen).centerCrop()) 58 | .into(binding.songImgNP) 59 | binding.songNameNP.text = PlayerActivity.musicListPA[PlayerActivity.songPosition].title 60 | if(PlayerActivity.isPlaying) binding.playPauseBtnNP.setIconResource(R.drawable.pause_icon) 61 | else binding.playPauseBtnNP.setIconResource(R.drawable.play_icon) 62 | } 63 | } 64 | 65 | private fun playMusic(){ 66 | PlayerActivity.isPlaying = true 67 | PlayerActivity.musicService!!.mediaPlayer!!.start() 68 | binding.playPauseBtnNP.setIconResource(R.drawable.pause_icon) 69 | PlayerActivity.musicService!!.showNotification(R.drawable.pause_icon) 70 | } 71 | private fun pauseMusic(){ 72 | PlayerActivity.isPlaying = false 73 | PlayerActivity.musicService!!.mediaPlayer!!.pause() 74 | binding.playPauseBtnNP.setIconResource(R.drawable.play_icon) 75 | PlayerActivity.musicService!!.showNotification(R.drawable.play_icon) 76 | } 77 | } -------------------------------------------------------------------------------- /app/src/main/java/com/harshRajpurohit/musicPlayer/PlayNext.kt: -------------------------------------------------------------------------------- 1 | package com.harshRajpurohit.musicPlayer 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.appcompat.app.AppCompatActivity 6 | import androidx.recyclerview.widget.GridLayoutManager 7 | import com.harshRajpurohit.musicPlayer.databinding.ActivityPlayNextBinding 8 | 9 | class PlayNext : AppCompatActivity() { 10 | 11 | companion object{ 12 | var playNextList: ArrayList = ArrayList() 13 | } 14 | 15 | override fun onCreate(savedInstanceState: Bundle?) { 16 | super.onCreate(savedInstanceState) 17 | setTheme(MainActivity.currentTheme[MainActivity.themeIndex]) 18 | val binding = ActivityPlayNextBinding.inflate(layoutInflater) 19 | setContentView(binding.root) 20 | 21 | binding.playNextRV.setHasFixedSize(true) 22 | binding.playNextRV.setItemViewCacheSize(13) 23 | binding.playNextRV.layoutManager = GridLayoutManager(this, 4) 24 | binding.playNextRV.adapter = FavouriteAdapter(this, playNextList, playNext = true) 25 | 26 | if(playNextList.isNotEmpty()) 27 | binding.instructionPN.visibility = View.GONE 28 | 29 | binding.backBtnPN.setOnClickListener { 30 | finish() 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /app/src/main/java/com/harshRajpurohit/musicPlayer/PlaylistActivity.kt: -------------------------------------------------------------------------------- 1 | package com.harshRajpurohit.musicPlayer 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.widget.Toast 7 | import androidx.appcompat.app.AppCompatActivity 8 | import androidx.recyclerview.widget.GridLayoutManager 9 | import com.google.android.material.dialog.MaterialAlertDialogBuilder 10 | import com.harshRajpurohit.musicPlayer.databinding.ActivityPlaylistBinding 11 | import com.harshRajpurohit.musicPlayer.databinding.AddPlaylistDialogBinding 12 | import java.text.SimpleDateFormat 13 | import java.util.* 14 | import kotlin.collections.ArrayList 15 | 16 | class PlaylistActivity : AppCompatActivity() { 17 | 18 | private lateinit var binding: ActivityPlaylistBinding 19 | private lateinit var adapter: PlaylistViewAdapter 20 | 21 | companion object{ 22 | var musicPlaylist: MusicPlaylist = MusicPlaylist() 23 | } 24 | 25 | override fun onCreate(savedInstanceState: Bundle?) { 26 | super.onCreate(savedInstanceState) 27 | setTheme(MainActivity.currentTheme[MainActivity.themeIndex]) 28 | binding = ActivityPlaylistBinding.inflate(layoutInflater) 29 | setContentView(binding.root) 30 | binding.playlistRV.setHasFixedSize(true) 31 | binding.playlistRV.setItemViewCacheSize(13) 32 | binding.playlistRV.layoutManager = GridLayoutManager(this@PlaylistActivity, 2) 33 | adapter = PlaylistViewAdapter(this, playlistList = musicPlaylist.ref) 34 | binding.playlistRV.adapter = adapter 35 | binding.backBtnPLA.setOnClickListener { finish() } 36 | binding.addPlaylistBtn.setOnClickListener { customAlertDialog() } 37 | 38 | if(musicPlaylist.ref.isNotEmpty()) binding.instructionPA.visibility = View.GONE 39 | } 40 | private fun customAlertDialog(){ 41 | val customDialog = LayoutInflater.from(this@PlaylistActivity).inflate(R.layout.add_playlist_dialog, binding.root, false) 42 | val binder = AddPlaylistDialogBinding.bind(customDialog) 43 | val builder = MaterialAlertDialogBuilder(this) 44 | val dialog = builder.setView(customDialog) 45 | .setTitle("Playlist Details") 46 | .setPositiveButton("ADD"){ dialog, _ -> 47 | val playlistName = binder.playlistName.text 48 | val createdBy = binder.yourName.text 49 | if(playlistName != null && createdBy != null) 50 | if(playlistName.isNotEmpty() && createdBy.isNotEmpty()) 51 | { 52 | addPlaylist(playlistName.toString(), createdBy.toString()) 53 | } 54 | dialog.dismiss() 55 | }.create() 56 | dialog.show() 57 | setDialogBtnBackground(this, dialog) 58 | 59 | } 60 | private fun addPlaylist(name: String, createdBy: String){ 61 | var playlistExists = false 62 | for(i in musicPlaylist.ref) { 63 | if (name == i.name){ 64 | playlistExists = true 65 | break 66 | } 67 | } 68 | if(playlistExists) Toast.makeText(this, "Playlist Exist!!", Toast.LENGTH_SHORT).show() 69 | else { 70 | val tempPlaylist = Playlist() 71 | tempPlaylist.name = name 72 | tempPlaylist.playlist = ArrayList() 73 | tempPlaylist.createdBy = createdBy 74 | val calendar = Calendar.getInstance().time 75 | val sdf = SimpleDateFormat("dd MMM yyyy", Locale.ENGLISH) 76 | tempPlaylist.createdOn = sdf.format(calendar) 77 | musicPlaylist.ref.add(tempPlaylist) 78 | adapter.refreshPlaylist() 79 | } 80 | } 81 | 82 | override fun onResume() { 83 | super.onResume() 84 | adapter.notifyDataSetChanged() 85 | } 86 | } -------------------------------------------------------------------------------- /app/src/main/java/com/harshRajpurohit/musicPlayer/PlaylistDetails.kt: -------------------------------------------------------------------------------- 1 | package com.harshRajpurohit.musicPlayer 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Intent 5 | import android.os.Bundle 6 | import android.view.View 7 | import androidx.appcompat.app.AppCompatActivity 8 | import androidx.recyclerview.widget.LinearLayoutManager 9 | import com.bumptech.glide.Glide 10 | import com.bumptech.glide.request.RequestOptions 11 | import com.google.android.material.dialog.MaterialAlertDialogBuilder 12 | import com.google.gson.GsonBuilder 13 | import com.harshRajpurohit.musicPlayer.databinding.ActivityPlaylistDetailsBinding 14 | 15 | class PlaylistDetails : AppCompatActivity() { 16 | 17 | private lateinit var binding: ActivityPlaylistDetailsBinding 18 | private lateinit var adapter: MusicAdapter 19 | 20 | companion object{ 21 | var currentPlaylistPos: Int = -1 22 | } 23 | 24 | override fun onCreate(savedInstanceState: Bundle?) { 25 | super.onCreate(savedInstanceState) 26 | setTheme(MainActivity.currentTheme[MainActivity.themeIndex]) 27 | binding = ActivityPlaylistDetailsBinding.inflate(layoutInflater) 28 | setContentView(binding.root) 29 | currentPlaylistPos = intent.extras?.get("index") as Int 30 | try{PlaylistActivity.musicPlaylist.ref[currentPlaylistPos].playlist = 31 | checkPlaylist(playlist = PlaylistActivity.musicPlaylist.ref[currentPlaylistPos].playlist)} 32 | catch(e: Exception){} 33 | binding.playlistDetailsRV.setItemViewCacheSize(10) 34 | binding.playlistDetailsRV.setHasFixedSize(true) 35 | binding.playlistDetailsRV.layoutManager = LinearLayoutManager(this) 36 | adapter = MusicAdapter(this, PlaylistActivity.musicPlaylist.ref[currentPlaylistPos].playlist, playlistDetails = true) 37 | binding.playlistDetailsRV.adapter = adapter 38 | binding.backBtnPD.setOnClickListener { finish() } 39 | binding.shuffleBtnPD.setOnClickListener { 40 | val intent = Intent(this, PlayerActivity::class.java) 41 | intent.putExtra("index", 0) 42 | intent.putExtra("class", "PlaylistDetailsShuffle") 43 | startActivity(intent) 44 | } 45 | binding.addBtnPD.setOnClickListener { 46 | startActivity(Intent(this, SelectionActivity::class.java)) 47 | } 48 | binding.removeAllPD.setOnClickListener { 49 | val builder = MaterialAlertDialogBuilder(this) 50 | builder.setTitle("Remove") 51 | .setMessage("Do you want to remove all songs from playlist?") 52 | .setPositiveButton("Yes"){ dialog, _ -> 53 | PlaylistActivity.musicPlaylist.ref[currentPlaylistPos].playlist.clear() 54 | adapter.refreshPlaylist() 55 | dialog.dismiss() 56 | } 57 | .setNegativeButton("No"){dialog, _ -> 58 | dialog.dismiss() 59 | } 60 | val customDialog = builder.create() 61 | customDialog.show() 62 | 63 | setDialogBtnBackground(this, customDialog) 64 | } 65 | } 66 | 67 | @SuppressLint("SetTextI18n") 68 | override fun onResume() { 69 | super.onResume() 70 | binding.playlistNamePD.text = PlaylistActivity.musicPlaylist.ref[currentPlaylistPos].name 71 | binding.moreInfoPD.text = "Total ${adapter.itemCount} Songs.\n\n" + 72 | "Created On:\n${PlaylistActivity.musicPlaylist.ref[currentPlaylistPos].createdOn}\n\n" + 73 | " -- ${PlaylistActivity.musicPlaylist.ref[currentPlaylistPos].createdBy}" 74 | if(adapter.itemCount > 0) 75 | { 76 | Glide.with(this) 77 | .load(PlaylistActivity.musicPlaylist.ref[currentPlaylistPos].playlist[0].artUri) 78 | .apply(RequestOptions().placeholder(R.drawable.music_player_icon_slash_screen).centerCrop()) 79 | .into(binding.playlistImgPD) 80 | binding.shuffleBtnPD.visibility = View.VISIBLE 81 | } 82 | adapter.notifyDataSetChanged() 83 | //for storing favourites data using shared preferences 84 | val editor = getSharedPreferences("FAVOURITES", MODE_PRIVATE).edit() 85 | val jsonStringPlaylist = GsonBuilder().create().toJson(PlaylistActivity.musicPlaylist) 86 | editor.putString("MusicPlaylist", jsonStringPlaylist) 87 | editor.apply() 88 | } 89 | } -------------------------------------------------------------------------------- /app/src/main/java/com/harshRajpurohit/musicPlayer/PlaylistViewAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.harshRajpurohit.musicPlayer 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import android.view.LayoutInflater 6 | import android.view.ViewGroup 7 | import androidx.core.content.ContextCompat 8 | import androidx.recyclerview.widget.RecyclerView 9 | import com.bumptech.glide.Glide 10 | import com.bumptech.glide.request.RequestOptions 11 | import com.google.android.material.dialog.MaterialAlertDialogBuilder 12 | import com.harshRajpurohit.musicPlayer.databinding.PlaylistViewBinding 13 | 14 | class PlaylistViewAdapter(private val context: Context, private var playlistList: ArrayList) : RecyclerView.Adapter() { 15 | 16 | class MyHolder(binding: PlaylistViewBinding) : RecyclerView.ViewHolder(binding.root) { 17 | val image = binding.playlistImg 18 | val name = binding.playlistName 19 | val root = binding.root 20 | val delete = binding.playlistDeleteBtn 21 | } 22 | 23 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyHolder { 24 | return MyHolder(PlaylistViewBinding.inflate(LayoutInflater.from(context), parent, false)) 25 | } 26 | 27 | override fun onBindViewHolder(holder: MyHolder, position: Int) { 28 | if(MainActivity.themeIndex == 4){ 29 | holder.root.strokeColor = ContextCompat.getColor(context, R.color.white) 30 | } 31 | holder.name.text = playlistList[position].name 32 | holder.name.isSelected = true 33 | holder.delete.setOnClickListener { 34 | val builder = MaterialAlertDialogBuilder(context) 35 | builder.setTitle(playlistList[position].name) 36 | .setMessage("Do you want to delete playlist?") 37 | .setPositiveButton("Yes"){ dialog, _ -> 38 | PlaylistActivity.musicPlaylist.ref.removeAt(position) 39 | refreshPlaylist() 40 | dialog.dismiss() 41 | } 42 | .setNegativeButton("No"){dialog, _ -> 43 | dialog.dismiss() 44 | } 45 | val customDialog = builder.create() 46 | customDialog.show() 47 | 48 | setDialogBtnBackground(context, customDialog) 49 | } 50 | holder.root.setOnClickListener { 51 | val intent = Intent(context, PlaylistDetails::class.java) 52 | intent.putExtra("index", position) 53 | ContextCompat.startActivity(context, intent, null) 54 | } 55 | if(PlaylistActivity.musicPlaylist.ref[position].playlist.size > 0){ 56 | Glide.with(context) 57 | .load(PlaylistActivity.musicPlaylist.ref[position].playlist[0].artUri) 58 | .apply(RequestOptions().placeholder(R.drawable.music_player_icon_slash_screen).centerCrop()) 59 | .into(holder.image) 60 | } 61 | } 62 | 63 | override fun getItemCount(): Int { 64 | return playlistList.size 65 | } 66 | fun refreshPlaylist(){ 67 | playlistList = ArrayList() 68 | playlistList.addAll(PlaylistActivity.musicPlaylist.ref) 69 | notifyDataSetChanged() 70 | } 71 | } -------------------------------------------------------------------------------- /app/src/main/java/com/harshRajpurohit/musicPlayer/SelectionActivity.kt: -------------------------------------------------------------------------------- 1 | package com.harshRajpurohit.musicPlayer 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import androidx.appcompat.widget.SearchView 6 | import androidx.core.content.ContextCompat 7 | import androidx.recyclerview.widget.LinearLayoutManager 8 | import com.harshRajpurohit.musicPlayer.databinding.ActivitySelectionBinding 9 | 10 | class SelectionActivity : AppCompatActivity() { 11 | 12 | private lateinit var binding: ActivitySelectionBinding 13 | private lateinit var adapter: MusicAdapter 14 | 15 | override fun onCreate(savedInstanceState: Bundle?) { 16 | super.onCreate(savedInstanceState) 17 | binding = ActivitySelectionBinding.inflate(layoutInflater) 18 | setTheme(MainActivity.currentTheme[MainActivity.themeIndex]) 19 | setContentView(binding.root) 20 | binding.selectionRV.setItemViewCacheSize(30) 21 | binding.selectionRV.setHasFixedSize(true) 22 | binding.selectionRV.layoutManager = LinearLayoutManager(this) 23 | adapter = MusicAdapter(this, MainActivity.MusicListMA, selectionActivity = true) 24 | binding.selectionRV.adapter = adapter 25 | binding.backBtnSA.setOnClickListener { finish() } 26 | //for search View 27 | binding.searchViewSA.setOnQueryTextListener(object : SearchView.OnQueryTextListener{ 28 | override fun onQueryTextSubmit(query: String?): Boolean = true 29 | override fun onQueryTextChange(newText: String?): Boolean { 30 | MainActivity.musicListSearch = ArrayList() 31 | if(newText != null){ 32 | val userInput = newText.lowercase() 33 | for (song in MainActivity.MusicListMA) 34 | if(song.title.lowercase().contains(userInput)) 35 | MainActivity.musicListSearch.add(song) 36 | MainActivity.search = true 37 | adapter.updateMusicList(searchList = MainActivity.musicListSearch) 38 | } 39 | return true 40 | } 41 | }) 42 | } 43 | 44 | override fun onResume() { 45 | super.onResume() 46 | //for black theme checking 47 | if(MainActivity.themeIndex == 4) 48 | { 49 | binding.searchViewSA.backgroundTintList = ContextCompat.getColorStateList(this, R.color.white) 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /app/src/main/java/com/harshRajpurohit/musicPlayer/SettingsActivity.kt: -------------------------------------------------------------------------------- 1 | package com.harshRajpurohit.musicPlayer 2 | 3 | import android.graphics.Color 4 | import android.os.Bundle 5 | import androidx.appcompat.app.AppCompatActivity 6 | import com.google.android.material.dialog.MaterialAlertDialogBuilder 7 | import com.harshRajpurohit.musicPlayer.databinding.ActivitySettingsBinding 8 | 9 | class SettingsActivity : AppCompatActivity() { 10 | 11 | lateinit var binding: ActivitySettingsBinding 12 | 13 | override fun onCreate(savedInstanceState: Bundle?) { 14 | super.onCreate(savedInstanceState) 15 | setTheme(MainActivity.currentThemeNav[MainActivity.themeIndex]) 16 | binding = ActivitySettingsBinding.inflate(layoutInflater) 17 | setContentView(binding.root) 18 | supportActionBar?.title = "Settings" 19 | when(MainActivity.themeIndex){ 20 | 0 -> binding.coolPinkTheme.setBackgroundColor(Color.YELLOW) 21 | 1 -> binding.coolBlueTheme.setBackgroundColor(Color.YELLOW) 22 | 2 -> binding.coolPurpleTheme.setBackgroundColor(Color.YELLOW) 23 | 3 -> binding.coolGreenTheme.setBackgroundColor(Color.YELLOW) 24 | 4 -> binding.coolBlackTheme.setBackgroundColor(Color.YELLOW) 25 | } 26 | binding.coolPinkTheme.setOnClickListener { saveTheme(0) } 27 | binding.coolBlueTheme.setOnClickListener { saveTheme(1) } 28 | binding.coolPurpleTheme.setOnClickListener { saveTheme(2) } 29 | binding.coolGreenTheme.setOnClickListener { saveTheme(3) } 30 | binding.coolBlackTheme.setOnClickListener { saveTheme(4) } 31 | binding.versionName.text = setVersionDetails() 32 | binding.sortBtn.setOnClickListener { 33 | val menuList = arrayOf("Recently Added", "Song Title", "File Size") 34 | var currentSort = MainActivity.sortOrder 35 | val builder = MaterialAlertDialogBuilder(this) 36 | builder.setTitle("Sorting") 37 | .setPositiveButton("OK"){ _, _ -> 38 | val editor = getSharedPreferences("SORTING", MODE_PRIVATE).edit() 39 | editor.putInt("sortOrder", currentSort) 40 | editor.apply() 41 | } 42 | .setSingleChoiceItems(menuList, currentSort){ _,which-> 43 | currentSort = which 44 | } 45 | val customDialog = builder.create() 46 | customDialog.show() 47 | 48 | setDialogBtnBackground(this, customDialog) 49 | } 50 | } 51 | 52 | private fun saveTheme(index: Int){ 53 | if(MainActivity.themeIndex != index){ 54 | val editor = getSharedPreferences("THEMES", MODE_PRIVATE).edit() 55 | editor.putInt("themeIndex", index) 56 | editor.apply() 57 | val builder = MaterialAlertDialogBuilder(this) 58 | builder.setTitle("Apply Theme") 59 | .setMessage("Do you want to apply theme?") 60 | .setPositiveButton("Yes"){ _, _ -> 61 | exitApplication() 62 | } 63 | .setNegativeButton("No"){dialog, _ -> 64 | dialog.dismiss() 65 | } 66 | val customDialog = builder.create() 67 | customDialog.show() 68 | 69 | setDialogBtnBackground(this, customDialog) 70 | } 71 | } 72 | private fun setVersionDetails():String{ 73 | return "Version Name: ${BuildConfig.VERSION_NAME}" 74 | } 75 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/about_icon.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/add_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/back_icon.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/booster_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/circle_icon.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/custom_search_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/delete_icon.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/equalizer_icon.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/exit_icon.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/favourite_empty_icon.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/favourite_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/feedback_icon.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/gradient_black.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/gradient_blue.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/gradient_green.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/gradient_pink.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/gradient_purple.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/music_icon.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/music_player_icon_slash_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HarshAndroid/MusicPlayer-Android-Kotlin/cb15fa4656db774c707b98921ad6246b8b7c1ff6/app/src/main/res/drawable/music_player_icon_slash_screen.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/next_icon.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/pause_icon.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/play_icon.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/play_next_icon.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 13 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/playlist_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/previous_icon.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/remove_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/repeat_icon.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/scroll_bar_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/search_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/settings_icon.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/share_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shuffle_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/sort_icon.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/splash_screen.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/timer_icon.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/volume_icon.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_about.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_favourite.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 19 | 20 | 29 | 30 | 39 | 40 | 41 | 52 | 53 | 69 | 70 | 84 | 85 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_feedback.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 18 | 19 | 27 | 28 | 35 |