How to make countdowntimer for android? - android

im new at coding, i dont know even basics but
I need make somekind 20 seconds countdown timer and i didin't found any working tutorials.
I tried my best but always some error came up.

pass time in milliseconds.
var countDownTimer: CountDownTimer? = null //declare it as global variable
fun startCountDown(time: Long) {
countDownTimer = object: CountDownTimer(time,1000){
override fun onFinish() {
Timber.v("Countdown: Finished")
visibility = View.GONE
}
override fun onTick(millisUntilFinished: Long) {
Timber.v("Countdown: $millisUntilFinished")
visibility = View.VISIBLE
updateTimeView(millisUntilFinished)
}
}
countDownTimer?.start()
}
For example:
startCountDown(30000), will countdown from 30 to 0.
Note:
Don't forget to stop the timer when your app stops:
override fun onStop() {
super.onStop()
countDownTimer?.cancel()
}

Related

Proper usage of OnBackPressedDispatcher

Sometimes, I want to handle the back button being pressed by the user myself. My (example) code looks something like this:
class TestActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.test)
}
override fun onBackPressed() {
if (checkSomeCondition()) {
// do nothing
} else {
super.onBackPressed()
}
}
private fun checkSomeCondition() = false
}
I get notified when back is pressed, and then I decide if I want to handle it, or let the system handle it by calling super.onBackPressed().
Since onBackPressed() is now deprecated, I replace it by using the OnBackPressedDispatcher:
class TestActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.test)
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (checkSomeCondition()) {
// do nothing
} else {
onBackPressedDispatcher.onBackPressed()
}
}
})
}
private fun checkSomeCondition() = false
}
The problem with this code: by calling onBackPressedDispatcher.onBackPressed(), I call my own callback, creating an infinite loop instead of handing this over to the system like I would with super.onBackPressed().
This can be circumvented when temporarily disabling my callback like:
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (checkSomeCondition()) {
// do nothing
} else {
this.isEnabled = false
onBackPressedDispatcher.onBackPressed()
this.isEnabled = true
}
}
})
but that seems rather awkward and doesn't seem like it was intended that way.
What's the correct usage of the OnBackPressedDispatcher here that lets me either handle the back button press myself or hand it over to the system?
PS: I have seen this question about the deprecation of onBackPressed, but it doesn't answer my much more specific question.
As per the Basics of System Back video, the whole point of the Predictive Back gesture is to know ahead of time what is going to handle back.
That means that you should never have just-in-time logic such as checkSomeCondition as part of your call to handleOnBackPressed.
Instead, as explained in the Custom back navigation documentation, you should be updating the isEnabled state of your OnBackPressedCallback ahead of time whenever the conditions you used to check in checkSomeCondition changed. This ensures that your callback is only invoked when your condition is already true and is disabled when the condition is false, thus allowing the default behavior to occur.
class TestActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.test)
// Instead of always setting enabled to true at the beginning,
// you need to check the state ahead of time to know what the
// initial enabled state should be
val isConditionAlreadySet = checkSomeCondition()
val callback = object : OnBackPressedCallback(
isConditionAlreadySet
) {
override fun handleOnBackPressed() {
// Since you've handled isEnabled correctly, you know
// your condition is set correctly here, so you can
// unconditionally do what you need to do to handle back
}
}
// This is the key part - now you know ahead of time
// when the condition changes, which lets you control
// when you want to handle back or not
setOnSomeConditionChangedListener { isConditionMet ->
callback.isEnabled = isConditionMet
}
onBackPressedDispatcher.addCallback(this, callback)
}
private fun checkSomeCondition() = false
private fun setOnSomeConditionChangedListener(
(isConditionMet: Boolean) -> Unit
) {
// The implementation here will depend on what
// conditions checkSomeCondition() actually depends on
}
}

Timer reset when fragment is destroyed

I want to recreate a timer like Google Authenticator, which never stops and if the app is killed and you reopen it, the timer still working.
I tried to make a timer but every time I destroy the fragment, it resets. How it is possible to do this?
val timer = object: CountDownTimer(10000,1000){
override fun onTick(millisecondsLeft:Long){
//Do something
}
override fun onFinish(){
this.start //this resets the timer when it reach 0
}
}
timer.start()
It's not a timer. It simply uses realtime clock.
The "start" event is that you take the current RTC value, save it and then display the difference as "elapsed time". This way, there is nothing to worry about when the app is not running. On app restart, re-read the value and you're there.
Keep your timer in a ViewModel, which is Lifecycle aware, so your timer will keep working as long as the parent activity "exists".
class YourViewModel: ViewModel() {
private val timer: CountDownTimer
init {
timer = object: CountDownTimer(10000, 1000) {
override fun onTick(p0: Long) {
//Do something
}
override fun onFinish() {
this.start() //this resets the timer when it reach 0
}
}
}
override fun onCleared() {
super.onCleared()
timer.cancel()
}
}

Refactoring activities which differ only in data presented

I'm writing an Android app in Android Studio with Kotlin. As I'm new to this, I had a wrong perception that every new 'shot' needs a new activity. I'm writing a story game with multiple shots.
So, my activities differ only by data.
There 2 types of repeating parts:
1st: only picture on the screen is changing with next-button press.
Code:
class Kadr1Activity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_kadr01)
}
fun nextClick(view: View) {
val intentKadr2 = Intent(this, Kadr2Activity::class.java) //every next button intents next shot
startActivity(intentKadr2)
this.finish()
}
fun backClick(view: View){
val intentKadr0 = Intent(this, MainActivity::class.java) //every back button intents previous shot
startActivity(intentKadr0)
this.finish()
}
}
2nd: user should solve the task by clicking the correct answer.
Code:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_kadr13)
}
var flag = false
fun nextClick(view: View) {
val intentKadr14 = Intent(this, Kadr14Activity::class.java)
startActivity(intentKadr14)
this.finish()
}
fun backClick(view: View){
val intentKadr12 = Intent(this, Kadr12Activity::class.java)
startActivity(intentKadr12)
this.finish()
}
fun btn1Click(view: View){
ans131Btn.setImageResource(R.drawable.btn_13_1_chosen) //picture of chosen button is changed every time it is pressed
ans132Btn.setImageResource(R.drawable.btn_13_2)
ans133Btn.setImageResource(R.drawable.btn_13_3)
flag = false
}
fun btn2Click(view: View){
ans132Btn.setImageResource(R.drawable.btn_13_2_chosen)
ans131Btn.setImageResource(R.drawable.btn_13_1)
ans133Btn.setImageResource(R.drawable.btn_13_3)
flag = true
}
fun btn3Click(view: View){
ans133Btn.setImageResource(R.drawable.btn_13_3_chosen)
ans131Btn.setImageResource(R.drawable.btn_13_1)
ans132Btn.setImageResource(R.drawable.btn_13_2)
flag = false
}
}
I've read that it would be better to get rid of repeating parts of code and refactor it somehow.
Could anyone give me a hint where I can actually find how to refactor my code? Would be very grateful.

CountDownTimer is not cancelling

None of the other instances of this question are solving my problem. I have a Fragment that appears at the end of a transaction sequence. It is meant to close the app when a CountDownTimer contained within it counts down:
class TerminalFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onStart() {
super.onStart()
startOverButton.setOnClickListener {
returnToStart()
}
initUi()
startCountDown()
}
override fun onStop() {
super.onStop()
AppLog.i(TAG, "onStop()")
stopCountdown()
}
}
private fun startCountDown() {
terminalCountdown = object : CountDownTimer(5000, 1000) {
override fun onFinish() {
AppLog.i(TAG, "Terminal countdown finished")
(context as MainActivity).finish()
}
override fun onTick(millisUntilFinished: Long) {
}
}
.start()
}
private fun stopCountdown() {
AppLog.i(TAG, "stopCountDown() - Terminal countdown stopped")
terminalCountdown?.cancel()
terminalCountdown = null
}
private fun returnToStart() {
AppLog.i(TAG, "returnToStart()")
stopCountdown()
(context as MainActivity).restartFlow()
}
stopCountDown() is being called whenever the fragment is navigated away from, but it somehow survives sometimes and closes the app from another Fragment. Using logs, I've also discovered that there appears to be 2 instances of this countdown sometimes. How do I insure that this countdown is never active outside of this fragment?
As Shrey Greg mentioned in a comment, I was creating a new instance of CountdownTimer every time I wanted to restart it. This lost the reference to the previous instance, making it impossible to cancel.

How to call a function from within the same class activity in Kotlin? - How to loop CountDowntimer from its own class

What I am trying to do is restart the CountdownTimer object when it is finished. Therefor I put it inside a procedure 'goCountdown', so onFinish() it calls itself. However this gives the following problems:
A procedure goCountdown cannot be called from within the same class
B activity text cannot be updated with time from outside the MainActivity class.
See references '!!! nr !!!' in code below:
the 'goCountdown() function is found (recognized/doesnt give an
error) at place 1 even though the procedure is placed outside the class.
when goCountdown is placed inside the class at nr 2 it is not found at place nr 1 (gives error).
As it only works outside of the class, it is now impossible to update the text on the activity on every tick because the MainActivity isnt accessible.
Questions:
Why does Kotlin / Android Studio not recognize the 'goCountdown()' function when it is placed WITHIN
the same activity class?
Even if that will work, is there a way to acces the texts on the MainActivity from a top level procedure?
Besides my goal to make the timer loop I am trying to understand why it is not working. Thanks for explaining or pointing me to the explanation.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
tapAction.setOnClickListener{
boTimeRunning = !boTimeRunning
if(boTimeRunning ){
goCountdown() //!!! 1 !!! its fine to call it from here when its outside the class
}
}
//!!! 2 !!!but if fun goCountdown() block is placed here, it is not seen at !!! 1 !!! place
}
}
fun goCountdown(){
object : CountDownTimer1(timeSettings_set * 1000, 1000){
override fun onTick(p0: Long) {
MainActivity.txtBig.text = "sometext" //!!! 3 !!!this doesnt work, also when MainActivity is declared as a variable object.
}
override fun onFinish() {
goCountdown() //primary goal: restart the timer when its done
}
}.start()
}
Is txtBig kotlin synthetic view or a local variable or global variable?
if txtBig is kotlin synthetic view, you should be able to call it.
if txtBig isn't kotlin synthetic veiw, try this code.
class MainActivity : AppCompatActivity() {
private lateinit var txtBigcopy: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
txtBig = findViewById<TextView>(R.id.txt_big) // Your id in xml
txtBigcopy = txtBig
tapAction.setOnClickListener{
boTimeRunning = !boTimeRunning
if(boTimeRunning ){
goCountdown()
}
}
}
fun goCountdown(){
object : CountDownTimer1(timeSettings_set * 1000, 1000){
override fun onTick(p0: Long) {
txtBigcopy.text = "sometext"
}
override fun onFinish() {
goCountdown()
}
}.start()
}
}

Categories

Resources