I'm using CountdownTimer in my app to display remaining time until specific Date. But Date is only 2 hours from current time, but if I convert millisUntilFinished to hours, it says 9 hours. Date is in UTC format.
remainingTimer = object : CountDownTimer(dateTime.time, 1000) {
override fun onTick(millisUntilFinished: Long) {
remTime = millisUntilFinished
notifyChanged(PAYLOAD_UPDATE_REM_TIME)
}
override fun onFinish() {
swapTimers()
}
}.start()
val hours = ((remTime / (1000 * 60 * 60)).rem(24))
Here you have to specify timer time for how long (2 hour from now = 2*60*60*1000 millis) you want to run
if you convert date in millis its not gonna work the way you want as it return date in millis,
remainingTimer = object : CountDownTimer(2*60*60*1000, 1000) {
override fun onTick(millisUntilFinished: Long) {
remTime = millisUntilFinished
notifyChanged(PAYLOAD_UPDATE_REM_TIME)
}
override fun onFinish() {
swapTimers()
}
}.start()
val hours = ((remTime / (1000 * 60 * 60)).rem(24))
Related
I am experimenting with CountDownTimer in jetpack compose with the following code
#Composable
fun Timer() {
val millisInFuture: Long = 10 * 1000 // TODO: get actual value
val timeData = remember {
mutableStateOf(millisInFuture)
}
val countDownTimer =
object : CountDownTimer(millisInFuture, 1000) {
override fun onTick(millisUntilFinished: Long) {
Log.d("TAG", "onTick: ")
timeData.value = millisInFuture
}
override fun onFinish() {
}
}
DisposableEffect(key1 = "key") {
countDownTimer.start()
onDispose {
countDownTimer.cancel()
}
}
Text(
text = timeData.value.toString()
)
}
In the logcat I am able to see the timer is ticking but the UI is not updating .
Please explain why there is on recomposition on changing the value of state variable.
Well, Within the CountDownTimer, instead of setting millisInFuture, you should set millisUntilFinished. That variable holds the updated value, the millisInFuture never changes
timeData.value = millisUntilFinished
You can try this code to implement a Countdown timer:
val time = (timerDate.time).minus(Calendar.getInstance().timeInMillis)
var timer by remember { mutableStateOf(time) }
LaunchedEffect(key1 = timer) {
if (timer > 0) {
delay(1000L)
timer -= 1000L
}
}
val secMilSec: Long = 1000
val minMilSec = 60 * secMilSec
val hourMilSec = 60 * minMilSec
val dayMilSec = 24 * hourMilSec
val hours = (time % dayMilSec / hourMilSec).toInt()
val minutes = (time % dayMilSec % hourMilSec / minMilSec).toInt()
val seconds = (time % dayMilSec % hourMilSec % minMilSec / secMilSec).toInt()
Text(text = String.format("%02d:%02d:%02d", hours, minutes, seconds))
Composable only recompose when there is state change either from the Composable function param or by the value change of State<T> inside the Composable itself like mutableStateOf() or mutableStateListOf(). In your case, you haven't start the countDownTimer itself. Try to call countDownTimer.start() inside the DisposableEffect. Second you set the timeData with the wrong value, try to set it with millisUntilFinished
I need to add 10 seconds:
private fun countdowntimer() {
object : CountDownTimer((time), 1000) {
override fun onTick(millisUntilFinished: Long) {
if ((correct_answers % 5).equals(0) && correct_answers !=0 ) {
// i want to add 10 seconds to countdown timer object also
mTextField.setText("seconds remaining: " + (millisUntilFinished+10000 )/ 1000)
} else {
mTextField.setText("seconds remaining: " + millisUntilFinished / 1000)
}
}
override fun onFinish() {
mTextField.setText("done")
//opengameover()
}
}.start()
}
I am not sure but try to multiply with 10 with your calculation like this
(millisUntilFinished+10000 )*10/ 1000
I am trying to pull a String value equal to the moment the chronometer stops as in "01:21" but the elapsed time gives an integer value, as in "11322".
val chronometer = findViewById<Chronometer>(R.id.chronometer)
chronometer.base = SystemClock.elapsedRealtime()
chronometer.format = "%s"
chronometer.start()
button.setOnClickListener {
val elapsedTime = SystemClock.elapsedRealtime() - chronometer.base
header.text = elapsedtime.toString()
Toast.makeText(this,"$elapsedTime",Toast.LENGTH_SHORT).show()
}
To be precise you get a long value and not an integer because both SystemClock.elapsedRealtime() and chronometer.base return a long value.
I have to disappoint you, currently there is no way to directly get the shown time of a chronometer, but of course you can convert the milliseconds you got to minutes and seconds, so you can show it again.
Here's my example of how it could work:
private fun calculateTime(chronometerMillis : Long) {
val minutes = TimeUnit.MILLISECONDS.toMinutes(chronometerMillis)
val seconds = TimeUnit.MILLISECONDS.toSeconds(chronometerMillis) - (minutes * 60)
println("Recreated time: $minutes:$seconds")
}
If you now call this method with the value 81000, which is 1 Minute and 21 Seconds (just like your chronometer), it prints Recreated time: 1:21.
To use it in your project just return a String:
private fun calculateTime(chronometerMillis : Long) : String {
val minutes = TimeUnit.MILLISECONDS.toMinutes(chronometerMillis)
val seconds = TimeUnit.MILLISECONDS.toSeconds(chronometerMillis) - (minutes * 60)
return "$minutes:$seconds"
}
private fun formattedChronometer(chronometer: Chronometer): String {
val elapsedTime = SystemClock.elapsedRealtime() - chronometer.base
val time = elapsedTime / 1000
val minutes = time / 60
val seconds = time % 60
return String.format("%02d:%02d",minutes,seconds)
}
Managed to solve it using this function after looking into the documentation
I have the following Kotlin code for simple countdown timer:
val thousand: Long = 1000
val timer = object: CountDownTimer(1800000, 1000) {
override fun onTick(millisUntilFinished: Long) {
var timeResult = millisUntilFinished/thousand
textTimer.text = "$timeResult"
}
override fun onFinish() {
textTimer.text = "Time is out"
}
}
timer.start()
textTimer.text = "$timer"
How to format 1800 seconds to 30 min : 00 sec in Kotlin?
You get the numbers by integer division and modulo (%)
and the formatting to 2 digits with padStart():
val secs = 1800
val formatted = "${(secs / 60).toString().padStart(2, '0')} min : ${(secs % 60).toString().padStart(2, '0')} sec"
println(formatted)
will print
30 min : 00 sec
Since you are targeting the JVM, you should use the Java 8 Date/Time Api. You could create a very concise function like this:
fun countdown(s: Long) = with(LocalTime.ofSecondOfDay(s)) {
String.format("%02d min :%02d sec", minute, second)
}
countdown(1800) // 30 min : 00 sec
Recommendation:
Don't do the calculations at call site (in onTick). This makes the code unecessary hard to understand. Create a separate function for that.
It's a simple calculation, but use what the standard libraries give you. Firstly, the code is optimized and secondly you can easily extend the function to calculate hours and so on. Untested, hand-drafted code is error prone.
Here's how my code looks like at the moment:
val timer = object: CountDownTimer(1800000, 1000) {
override fun onTick(millisUntilFinished: Long) {
val timeResult =
"${(millisUntilFinished / 1000 / 60).toString().padStart(2, '0')}:" +
"${(millisUntilFinished / 1000 % 60).toString().padStart(2, '0')} "
textTimer.text = "$timeResult"
}
override fun onFinish() {
textTimer.text = "Time is out"
}
}
timer.start()
textTimer.text = "$timer"
Following code can convert from milliseconds (which is what you have initially) to minutes and seconds:
val milliseconds: Long = 1800000
val minutes = milliseconds / 1000 / 60
val seconds = milliseconds / 1000 % 60
println("$minutes min : $seconds sec")
Output:
30 min : 0 sec
EDIT 1:
If you strictly need it from seconds to minutes and seconds, just remove the extra division by 1000.
The code for that will be:
val seconds: Long = 1800
val minutes = milliseconds / 60
val seconds = milliseconds % 60
println("$minutes min : $seconds sec")
The code in the original question had milliseconds in it that's why I mentioned it from Milliseconds.
EDIT 2:
Missed out the double digit formatting.
The answer by #forpas using .toString().padStart(2, '0') will give the correct formatted output. So, the correct code would be:
val milliseconds: Long = 1800000
val minutes = milliseconds / 1000 / 60
val seconds = milliseconds / 1000 % 60
println("{$minutes.toString().padStart(2, '0')} min : {$seconds.toString().padStart(2, '0')} sec")
Output:
30 min : 00 sec
I have two UNIX time stamp and i am using KOTLIN
1) old time - 1534854646
2) current time - 1534857527
Now i want the difference in hours and minutes.
val result = DateUtils.getRelativeTimeSpanString(1534854646, 1534857527, 0)
But it gives me 2 seconds but actual time difference is around 0 hour and 48 minutes.
I have also tried :
long mills = 1534857527 - 1534854646;
int hours = millis/(1000 * 60 * 60);
int mins = (mills/(1000*60)) % 60;
String diff = hours + ":" + mins;
But still it gives 0 hours and 0 minute.
Here is my solution, the code is written in Kotlin.
TimeInHours.kt
class TimeInHours(val hours: Int, val minutes: Int, val seconds: Int) {
override fun toString(): String {
return String.format("%dh : %02dm : %02ds", hours, minutes, seconds)
}
}
Write a function which converts time duration in seconds to TimeInHours.
fun convertFromDuration(timeInSeconds: Long): TimeInHours {
var time = timeInSeconds
val hours = time / 3600
time %= 3600
val minutes = time / 60
time %= 60
val seconds = time
return TimeInHours(hours.toInt(), minutes.toInt(), seconds.toInt())
}
Test.kt
val oldTime: Long = 1534854646
val currentTime: Long = 1534857527
val result = convertFromDuration(currentTime - oldTime)
Log.i("TAG", result.toString())
Output:
I/TAG: 0h : 48m : 01s
Do something like this, I have not tested this but it should work
long mills = 1534857527 - 1534854646;
String period = String.format("%02d:%02d",
TimeUnit.MILLISECONDS.toHours(mills),
TimeUnit.MILLISECONDS.toMinutes(mills) % TimeUnit.HOURS.toMinutes(1));
System.out.println("Duration hh:mm - " + period);