android kotlin - Palette API Example

MainActivity.kt

package com.cfsuman.kotlintutorials

import android.graphics.Bitmap
import android.os.Bundle
import android.view.View
import android.widget.AdapterView
import android.widget.ArrayAdapter
import androidx.appcompat.app.AppCompatActivity
import com.cfsuman.kotlintutorials.databinding.ActivityMainBinding


class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // Get the bitmap from assets
        val bitmap:Bitmap? = assetsBitmap("flower103.jpg")

        // Display the bitmap in image view
        binding.imageView.setImageBitmap(bitmap)

        // Button click listener
        binding.button.setOnClickListener{

            // If bitmap is not null then do the next task
            bitmap?.apply {
                // Get the palette colors
                val list =this.Colors

                // Get the palette color names
                val names = mutableListOf<String>()
                for (i in list){
                    names.add(i.name)
                }

                // Initializing an ArrayAdapter
                val adapter = ArrayAdapter(
                    applicationContext, // Context
                    android.R.layout.simple_spinner_item, // Layout
                    names // Array
                )

                // Set the drop down view resource
                adapter.setDropDownViewResource(
                    android.R.layout.simple_dropdown_item_1line)

                // Finally, data bind the spinner object with adapter
                binding.spinner.adapter = adapter;

                // Set an on item selected listener for spinner object
                binding.spinner.onItemSelectedListener = object: AdapterView
                .OnItemSelectedListener{
                    override fun onItemSelected(
                        parent:AdapterView<*>, view: View,
                        position: Int, id: Long){
                        val item = list[position]

                        binding.textLayout.setBackgroundColor(item.color)

                        binding.tvTitle.text = item.name

                        if (item.swatch!=null){
                            binding.tvTitle.setTextColor(
                                item.swatch.titleTextColor)
                            binding.tvBody.setTextColor(
                                item.swatch.bodyTextColor)
                        }
                    }

                    override fun onNothingSelected(parent: AdapterView<*>){
                        // Another interface callback
                    }
                }
            }
        }
    }
}
PaletteManager.kt

package com.cfsuman.kotlintutorials

import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import androidx.palette.graphics.Palette
import java.io.IOException
import java.io.InputStream


// Extension property to get palette colors from bitmap
val Bitmap.Colors:MutableList<PaletteColor>
    get() {
        // Initialize a list of palette color
        val list = mutableListOf<PaletteColor>()

        // Generate the palette from bitmap
        val palette = this.palette

        // Dark muted color and swatch
        val darkMutedColor = palette.getDarkMutedColor(Color.RED)
        val darkMutedSwatch = palette.darkMutedSwatch
        list.add(PaletteColor(
            "Dark muted color",darkMutedColor,darkMutedSwatch)
        )

        // Dark vibrant color and swatch
        val darkVibrantColor = palette.getDarkVibrantColor(Color.MAGENTA)
        val darkVibrantSwatch = palette.darkVibrantSwatch
        list.add(PaletteColor(
            "Dark vibrant color",
            darkVibrantColor,darkVibrantSwatch))

        // Dominant color and swatch
        val dominantColor = palette.getDominantColor(Color.BLUE)
        val dominantSwatch = palette.dominantSwatch
        list.add(PaletteColor("Dominant Color",
            dominantColor,dominantSwatch))

        // Light muted color and swatch
        val lightMutedColor = palette.getLightMutedColor(Color.YELLOW)
        val lightMutedSwatch = palette.lightMutedSwatch
        list.add(PaletteColor("Light muted color",
            lightMutedColor,lightMutedSwatch))

        // Light vibrant color and swatch
        val lightVibrantColor = palette.getLightVibrantColor(Color.CYAN)
        val lightVibrantSwatch = palette.lightVibrantSwatch
        list.add(PaletteColor("Light vibrant color",
            lightVibrantColor,lightVibrantSwatch))

        // Muted color and swatch
        val mutedColor = palette.getMutedColor(Color.GRAY)
        val mutedSwatch = palette.mutedSwatch
        list.add(PaletteColor("Muted color",mutedColor,mutedSwatch))
        return list
    }


// Initialize a data class to hold the palette color and swatch
data class PaletteColor(val name:String,
                        val color:Int, val swatch: Palette.Swatch?)


// Extension property to generate palette from bitmap
val Bitmap.palette: Palette
    get() {
        return Palette.from(this).generate()
    }


// Extension method get bitmap from assets
fun Context.assetsBitmap(path:String): Bitmap?{
    val inputStream: InputStream
    var bitmap: Bitmap? = null
    try {
        inputStream = assets.open(path)
        bitmap = BitmapFactory.decodeStream(inputStream)
    }catch (e: IOException){
        // Handle exception here
    }

    return bitmap
}
activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/root_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="12dp"
    android:background="#F8F8FF">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="match_parent"
        android:layout_height="175dp"
        android:scaleType="centerCrop"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="Generate Palette"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/imageView" />

    <Spinner
        android:id="@+id/spinner"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="@+id/button"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/button"
        app:layout_constraintTop_toTopOf="@+id/button" />

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/textLayout"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginTop="16dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/button">

        <TextView
            android:id="@+id/tvTitle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="8dp"
            android:textSize="24sp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/tvBody"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            android:padding="8dp"
            android:textSize="18sp"
            android:text="Lorem Ipsum is simply dummy text of the
            printing and typesetting industry. Lorem Ipsum has been
            the industry's standard dummy text ever since
            the 1500s, when an unknown printer took a galley
            of type and scrambled it to make a type specimen book.
            It has survived not only five centuries,
            but also the leap into electronic typesetting,
            remaining essentially unchanged."
            app:layout_constraintTop_toBottomOf="@+id/tvTitle"/>

    </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
build.gradle [app]

implementation 'androidx.palette:palette:1.0.0'

android kotlin - Vibrate phone programmatically

MainActivity.kt

package com.cfsuman.test

import android.content.Context
import android.graphics.Color
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
import android.os.VibrationEffect
import android.os.Build
import android.os.Vibrator


class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Set the root layout background color
        root_layout.setBackgroundColor(if(hasVibrator)Color.GREEN else Color.RED)

        // Button click listener
        button.setOnClickListener{
            // Vibrate the phone programmatically
            vibrate()
        }
    }
}


/*
    *** documentation source developer.android.com ***

    VibrationEffect
        A VibrationEffect describes a haptic effect to be performed by a Vibrator. These effects
        may be any number of things, from single shot vibrations to complex waveforms.


        VibrationEffect createOneShot (long milliseconds, int amplitude)
            Create a one shot vibration. One shot vibrations will vibrate constantly
            for the specified period of time at the specified amplitude, and then stop.

        Parameters
            milliseconds long : The number of milliseconds to vibrate. This must be a positive number.
            amplitude int : The strength of the vibration. This must be a value between 1 and 255, or DEFAULT_AMPLITUDE.

        Returns
            VibrationEffect The desired effect.

*/

// Extension method to vibrate a phone programmatically
fun Context.vibrate(milliseconds:Long = 500){
    val vibrator = this.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator

    // Check whether device/hardware has a vibrator
    val canVibrate:Boolean = vibrator.hasVibrator()

    if(canVibrate){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
            // void vibrate (VibrationEffect vibe)
            vibrator.vibrate(
                    VibrationEffect.createOneShot(
                            milliseconds,
                            // The default vibration strength of the device.
                            VibrationEffect.DEFAULT_AMPLITUDE
                    )
            )
        }else{
            // This method was deprecated in API level 26
            vibrator.vibrate(milliseconds)
        }
    }
}


// Extension property to check whether device has Vibrator
val Context.hasVibrator:Boolean
get() {
    val vibrator = this.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
    return vibrator.hasVibrator()
}
activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/root_layout"
    >
    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="80dp"
        android:text="Vibrate Phone"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
AndroidManifest.xml [Permission]

<uses-permission android:name="android.permission.VIBRATE"/>

android kotlin - Get alarm, ringtone, notification sound list

MainActivity.kt

package com.cfsuman.kotlintutorials

import android.app.Activity
import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.os.Bundle
import android.provider.MediaStore
import android.widget.*


class MainActivity : Activity() {
    lateinit var listView: ListView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // get the widgets reference from XML layout
        val buttonAlarm = findViewById<Button>(R.id.buttonAlarm)
        val buttonNotification = findViewById<Button>(R.id.buttonNotification)
        val buttonRingtone = findViewById<Button>(R.id.buttonRingtone)
        listView = findViewById<ListView>(R.id.listView)


        // Get the alarm sound list
        buttonAlarm.setOnClickListener{
            populateListView(sounds(MediaStore.Audio.Media.IS_ALARM))
        }


        // Get the notification sound list
        buttonNotification.setOnClickListener{
            populateListView(
                sounds(MediaStore.Audio.Media.IS_NOTIFICATION))
        }


        // Get the ringtone sound list
        buttonRingtone.setOnClickListener{
            populateListView(
                sounds(MediaStore.Audio.Media.IS_RINGTONE))
        }
    }


    // Display the specific sound list on list view
    private fun populateListView(list:MutableList<Sound>){
        // Get specific sound titles list
        val titles = mutableListOf<String>()
        for (music in list){titles.add(music.title)}

        // Display sound list on list view
        val adapter = ArrayAdapter(
            this,
            android.R.layout.simple_list_item_1,titles
        )
        listView.adapter = adapter
    }
}



// Extension method to get all specific type audio/sound files list
fun Context.sounds(type:String):MutableList<Sound>{
    // Initialize an empty mutable list of sounds
    val list:MutableList<Sound> = mutableListOf()


    // Get the internal storage media store audio uri
    //val uri: Uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
    val uri: Uri = MediaStore.Audio.Media.INTERNAL_CONTENT_URI


    // Non-zero if the audio file type match
    val selection = "$type != 0"


    // Sort the audio
    val sortOrder = MediaStore.Audio.Media.TITLE + " ASC"
    //val sortOrder = MediaStore.Audio.Media.TITLE + " DESC"


    // Query the storage for specific type audio files
    val cursor: Cursor? = this.contentResolver.query(
        uri, // Uri
        null, // Projection
        selection, // Selection
        null, // Selection arguments
        sortOrder // Sort order
    )


    // If query result is not empty
    cursor?.apply {
        if (this.moveToFirst()){
            val id:Int = this.getColumnIndex(
                MediaStore.Audio.Media._ID)
            val title:Int = this.getColumnIndex(
                MediaStore.Audio.Media.TITLE)

            // Now loop through the audio files
            do {
                val audioId:Long = this.getLong(id)
                val audioTitle:String = this.getString(title)

                // Add the current audio/sound to the list
                list.add(Sound(audioId,audioTitle))
            }while (this.moveToNext())
        }
    }


    // Finally, return the audio files list
    return  list
}



// Initialize a new data class to hold audio data
data class Sound(val id:Long, val title:String)
activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#DCDCDC"
    android:padding="24dp">

    <Button
        android:id="@+id/buttonAlarm"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Alarms"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/buttonNotification"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:text="Notifications"
        app:layout_constraintBottom_toBottomOf="@+id/buttonAlarm"
        app:layout_constraintStart_toEndOf="@+id/buttonAlarm" />

    <Button
        android:id="@+id/buttonRingtone"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:text="Ringtones"
        app:layout_constraintBottom_toBottomOf="@+id/buttonNotification"
        app:layout_constraintStart_toEndOf="@+id/buttonNotification" />

    <ListView
        android:id="@+id/listView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginTop="16dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/buttonAlarm" />

</androidx.constraintlayout.widget.ConstraintLayout>

android kotlin - Get all music on sd card

MainActivity.kt

package com.cfsuman.kotlintutorials

import android.app.Activity
import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.os.Bundle
import android.provider.MediaStore
import android.widget.*


class MainActivity : Activity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // get the widgets reference from XML layout
        val button = findViewById<Button>(R.id.button)
        val listView = findViewById<ListView>(R.id.listView)


        // Important : handle the runtime permission
        // Check runtime permission to read external storage


        // Button click listener
        button.setOnClickListener{
            // Disable the button itself
            it.isEnabled = false

            // Get the external storage/sd card music files list
            val list:MutableList<Music> = musicFiles()

            // Get the sd card music titles list
            val titles = mutableListOf<String>()
            for (music in list){titles.add(music.title)}

            // Display external storage music files list on list view
            val adapter = ArrayAdapter(
                this,
                android.R.layout.simple_list_item_1,titles
            )
            listView.adapter = adapter
        }
    }
}



// Get all music files list from external storage/sd card
fun Context.musicFiles():MutableList<Music>{
    // Initialize an empty mutable list of music
    val list:MutableList<Music> = mutableListOf()

    // Get the external storage media store audio uri
    val uri: Uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
    //val uri: Uri = MediaStore.Audio.Media.INTERNAL_CONTENT_URI

    // IS_MUSIC : Non-zero if the audio file is music
    val selection = MediaStore.Audio.Media.IS_MUSIC + "!= 0"

    // Sort the musics
    val sortOrder = MediaStore.Audio.Media.TITLE + " ASC"
    //val sortOrder = MediaStore.Audio.Media.TITLE + " DESC"

    // Query the external storage for music files
    val cursor: Cursor? = this.contentResolver.query(
        uri, // Uri
        null, // Projection
        selection, // Selection
        null, // Selection arguments
        sortOrder // Sort order
    )

    // If query result is not empty
    cursor?.apply {
        if (cursor.moveToFirst()){
            val id:Int = cursor.getColumnIndex(
                MediaStore.Audio.Media._ID)
            val title:Int = cursor.getColumnIndex(
                MediaStore.Audio.Media.TITLE)

            // Now loop through the music files
            do {
                val audioId:Long = cursor.getLong(id)
                val audioTitle:String = cursor.getString(title)

                // Add the current music to the list
                list.add(Music(audioId,audioTitle))
            }while (cursor.moveToNext())
        }
    }

    // Finally, return the music files list
    return  list
}


// Initialize a new data class to hold music data
data class Music(val id:Long, val title:String)
activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#DCDCDC"
    android:padding="24dp">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="SD Card Music List"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ListView
        android:id="@+id/listView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/button" />

</androidx.constraintlayout.widget.ConstraintLayout>
AndroidManifest.xml [Permission]

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

android kotlin - Screen brightness programmatically

MainActivity.kt

package com.cfsuman.kotlinexamples

import android.content.Context
import android.os.Build
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
import android.provider.Settings
import android.widget.SeekBar
import android.widget.Toast
import android.content.Intent
import android.net.Uri


class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Set the SeekBar initial progress from screen current brightness
        val brightness = brightness
        seek_bar.progress = brightness

        text_view.text = "Screen Brightness : $brightness"

        // If app has no permission to write system settings
        if(!canWrite){
            seek_bar.isEnabled = false
            allowWritePermission()
        }

        // Set a SeekBar change listener
        seek_bar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
            override fun onProgressChanged(seekBar: SeekBar, i: Int, b: Boolean) {
                // Display the current progress of SeekBar
                text_view.text = "Screen Brightness : $i"

                // Change the screen brightness
                if (canWrite){
                    setBrightness(i)
                }
            }

            override fun onStartTrackingTouch(seekBar: SeekBar) {

            }

            override fun onStopTrackingTouch(seekBar: SeekBar) {

            }
        })
    }
}



// Extension property to get write system settings permission status
val Context.canWrite:Boolean
get() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        return Settings.System.canWrite(this)
    }else{
        return true
    }
}


// Extension function to allow write system settings
fun Context.allowWritePermission(){
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            val intent = Intent(
                    Settings.ACTION_MANAGE_WRITE_SETTINGS,
                    Uri.parse("package:$packageName")
            )
            startActivity(intent)
    }
}


// Extension property to get screen brightness programmatically
val Context.brightness:Int
get() {
    return Settings.System.getInt(
            this.contentResolver,
            Settings.System.SCREEN_BRIGHTNESS,
            0
    )
}


// Extension method to set screen brightness programmatically
fun Context.setBrightness(value:Int):Unit{
    Settings.System.putInt(
            this.contentResolver,
            Settings.System.SCREEN_BRIGHTNESS,
            value
    )
}


// Extension function to show toast message quickly
fun Context.toast(message:String){
    Toast.makeText(this,message,Toast.LENGTH_SHORT).show()
}
activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/root_layout"
    android:background="#e7faef"
    android:padding="16dp"
    android:orientation="vertical"
    >
    <TextView
        android:id="@+id/text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:textStyle="bold"
        android:layout_marginBottom="25sp"
        />
    <SeekBar
        android:id="@+id/seek_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="255"
        android:padding="15dp"
        />
</LinearLayout>
AndroidManifest.xml [Permission]

<uses-permission android:name="android.permission.WRITE_SETTINGS"/>

android kotlin - Media player SeekBar example

MainActivity.kt

package com.cfsuman.kotlintutorials

import android.app.Activity
import android.content.Context
import android.media.MediaPlayer
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.widget.*


class MainActivity : Activity() {
    private lateinit var player: MediaPlayer
    private lateinit var runnable:Runnable
    private var handler: Handler = Handler(Looper.getMainLooper())
    lateinit var seekBar:SeekBar
    lateinit var tvPass:TextView
    lateinit var tvDue:TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // get the widgets reference from XML layout
        val buttonStart = findViewById<Button>(R.id.buttonStart)
        val buttonStop = findViewById<Button>(R.id.buttonStop)
        seekBar = findViewById(R.id.seekBar)
        val tvDuration = findViewById<TextView>(R.id.tvDuration)
        tvPass = findViewById(R.id.tvPass)
        tvDue = findViewById(R.id.tvDue)


        // Start the media player
        buttonStart.setOnClickListener{
            player = MediaPlayer.create(
                applicationContext,
                R.raw.test_sound
            )

            player.start()
            tvDuration.text = "${player.seconds} sec"
            initializeSeekBar()

            it.isEnabled = false
            buttonStop.isEnabled = true

            player.setOnCompletionListener {
                buttonStop.isEnabled = false
                buttonStart.isEnabled = true
                toast("end")
            }
        }


        // Stop the media player
        buttonStop.setOnClickListener{
            if(player.isPlaying){
                player.stop()
                player.reset()
                player.release()
                handler.removeCallbacks(runnable)

                it.isEnabled = false
                buttonStart.isEnabled = true
            }
        }


        // Seek bar change listener
        seekBar.setOnSeekBarChangeListener(
            object : SeekBar.OnSeekBarChangeListener {
            override fun onProgressChanged(
                seekBar: SeekBar, i: Int, b: Boolean) {
                if (b) {
                    player.seekTo(i * 1000)
                }
            }

            override fun onStartTrackingTouch(seekBar: SeekBar) {
            }

            override fun onStopTrackingTouch(seekBar: SeekBar) {
            }
        })
    }


    // Method to initialize seek bar and audio stats
    private fun initializeSeekBar() {
        seekBar.max = player.seconds

        runnable = Runnable {
            seekBar.progress = player.currentSeconds

            tvPass.text = "${player.currentSeconds} sec"
            val diff = player.seconds - player.currentSeconds
            tvDue.text = "$diff sec"

            handler.postDelayed(runnable, 1000)
        }
        handler.postDelayed(runnable, 1000)
    }
}



// Extension property to get
// media player duration in seconds
val MediaPlayer.seconds:Int
    get() {
        return this.duration / 1000
    }


// Extension property to get
// media player current position in seconds
val MediaPlayer.currentSeconds:Int
    get() {
        return this.currentPosition/1000
    }


// Extension function to show toast message quickly
fun Context.toast(message:String){
    Toast.makeText(this,message,Toast.LENGTH_SHORT).show()
}
activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#DCDCDC"
    android:padding="24dp">

    <TextView
        android:id="@+id/tvDuration"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
        app:layout_constraintBottom_toTopOf="@+id/seekBar"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        tools:text="Duration" />

    <TextView
        android:id="@+id/tvDue"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="24dp"
        app:layout_constraintBottom_toTopOf="@+id/seekBar"
        app:layout_constraintEnd_toEndOf="@+id/seekBar"
        tools:text="Due" />

    <TextView
        android:id="@+id/tvPass"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
        app:layout_constraintBottom_toTopOf="@+id/seekBar"
        app:layout_constraintStart_toStartOf="@+id/seekBar"
        tools:text="Pass" />

    <SeekBar
        android:id="@+id/seekBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:saveEnabled="false"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/buttonStart"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="Start"
        app:layout_constraintEnd_toStartOf="@+id/buttonStop"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/seekBar" />

    <Button
        android:id="@+id/buttonStop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Stop"
        app:layout_constraintBottom_toBottomOf="@+id/buttonStart"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/buttonStart" />

</androidx.constraintlayout.widget.ConstraintLayout>