How to Create a Timer using Jetpack Compose in Android? - GeeksforGeeks

18 Jul, 2021
Jetpack Compose is a modern toolkit for building native Android UI. Jetpack Compose simplifies and accelerates UI development on Android with less code, powerful tools, and intuitive Kotlin APIs. In this article, we are going to create a Timer using Jetpack Compose. Below is the sample video to show what we are going to build. 
Step 1: Create a new project
To create a new project in Android Studio using Jetpack Compose please refer to How to Create a New Project in Android Studio Canary Version with Jetpack Compose.
Step 2: Working with MainActivity.kt
Go to the MainActivity.kt file and refer to the following code. Below is the code for the MainActivity.kt file. Comments are added inside the code to understand the code in more detail.
Kotlin
import androidx.compose.ui.unit.dpimport androidx.activity.compose.setContentimport android.os.Bundleimport androidx.activity.ComponentActivityimport androidx.compose.material.Surfaceimport androidx.compose.material.Textimport androidx.compose.runtime.Composableimport androidx.compose.ui.Modifierimport androidx.compose.foundation.Canvasimport androidx.compose.foundation.layout.Boximport androidx.compose.foundation.layout.fillMaxSizeimport androidx.compose.foundation.layout.sizeimport androidx.compose.material.*import androidx.compose.runtime.*import androidx.compose.ui.Alignmentimport androidx.compose.ui.geometry.Offsetimport androidx.compose.ui.geometry.Sizeimport androidx.compose.ui.graphics.Colorimport androidx.compose.ui.graphics.PointModeimport androidx.compose.ui.graphics.StrokeCapimport androidx.compose.ui.graphics.drawscope.Strokeimport androidx.compose.ui.layout.onSizeChangedimport androidx.compose.ui.text.font.FontWeightimport androidx.compose.ui.unit.Dpimport androidx.compose.ui.unit.IntSizeimport androidx.compose.ui.unit.spimport kotlinx.coroutines.delayimport kotlin.math.PIimport kotlin.math.cosimport kotlin.math.sin  class MainActivity : ComponentActivity() {    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContent {            Surface(                color = Color(0xFF101010),                modifier = Modifier.fillMaxSize()            ) {                Box(                    contentAlignment = Alignment.Center                ) {                    // call the function Timer                     // and pass the values                    // it is defined below.                    Timer(                        totalTime = 100L * 1000L,                        handleColor = Color.Green,                        inactiveBarColor = Color.DarkGray,                        activeBarColor = Color(0xFF37B900),                        modifier = Modifier.size(200.dp)                    )                }            }        }    }}  // create a composable to // Draw arc and handle@Composablefun Timer(            // total time of the timer      totalTime: Long,           // circular handle color        handleColor: Color,           // color of inactive bar / progress bar     inactiveBarColor: Color,           // color of active bar     activeBarColor: Color,      modifier: Modifier = Modifier,         // set initial value to 1    initialValue: Float = 1f,     strokeWidth: Dp = 5.dp) {    // create variable for      // size of the composable    var size by remember {        mutableStateOf(IntSize.Zero)    }    // create variable for value    var value by remember {        mutableStateOf(initialValue)    }    // create variable for current time    var currentTime by remember {        mutableStateOf(totalTime)    }    // create variable for isTimerRunning    var isTimerRunning by remember {        mutableStateOf(false)    }    LaunchedEffect(key1 = currentTime, key2 = isTimerRunning) {        if(currentTime > 0 && isTimerRunning) {            delay(100L)            currentTime -= 100L            value = currentTime / totalTime.toFloat()        }    }    Box(        contentAlignment = Alignment.Center,        modifier = modifier            .onSizeChanged {                size = it            }    ) {        // draw the timer        Canvas(modifier = modifier) {            // draw the inactive arc with following parameters            drawArc(                color = inactiveBarColor, // assign the color                startAngle = -215f, // assign the start angle                sweepAngle = 250f, // arc angles                useCenter = false, // prevents our arc to connect at te ends                size = Size(size.width.toFloat(), size.height.toFloat()),                                // to make ends of arc round                style = Stroke(strokeWidth.toPx(), cap = StrokeCap.Round)             )            // draw the active arc with following parameters            drawArc(                color = activeBarColor, // assign the color                startAngle = -215f,  // assign the start angle                sweepAngle = 250f * value, // reduce the sweep angle                                              // with the current value                useCenter = false, // prevents our arc to connect at te ends                size = Size(size.width.toFloat(), size.height.toFloat()),                                // to make ends of arc round                style = Stroke(strokeWidth.toPx(), cap = StrokeCap.Round)             )            // calculate the value from arc pointer position            val center = Offset(size.width / 2f, size.height / 2f)            val beta = (250f * value + 145f) * (PI / 180f).toFloat()            val r = size.width / 2f            val a = cos(beta) * r            val b = sin(beta) * r            // draw the circular pointer/ cap            drawPoints(                listOf(Offset(center.x + a, center.y + b)),                pointMode = PointMode.Points,                color = handleColor,                strokeWidth = (strokeWidth * 3f).toPx(),                cap = StrokeCap.Round  // make the pointer round            )        }        // add value of the timer        Text(            text = (currentTime / 1000L).toString(),            fontSize = 44.sp,            fontWeight = FontWeight.Bold,            color = Color.White        )        // create button to start or stop the timer        Button(            onClick = {                if(currentTime <= 0L) {                    currentTime = totalTime                    isTimerRunning = true                } else {                    isTimerRunning = !isTimerRunning                }            },            modifier = Modifier.align(Alignment.BottomCenter),            // change button color            colors = ButtonDefaults.buttonColors(                backgroundColor = if (!isTimerRunning || currentTime <= 0L) {                    Color.Green                } else {                    Color.Red                }            )        ) {            Text(                // change the text of button based on values                text = if (isTimerRunning && currentTime >= 0L) "Stop"                else if (!isTimerRunning && currentTime >= 0L) "Start"                else "Restart"            )        }    }}
Output:
Android-Jetpack
Android
Kotlin
Android
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.
Comments
Old Comments
Retrofit with Kotlin Coroutine in Android
Android Listview in Java with Example
Flutter - Custom Bottom Navigation Bar
How to Read Data from SQLite Database in Android?
How to Change the Background Color After Clicking the Button in Android?
Android UI Layouts
Retrofit with Kotlin Coroutine in Android
Kotlin Array
Android Menus
MVP (Model View Presenter) Architecture Pattern in Android with Example