I want to start a timer (i think in this case CountDownTimer) as soon as I get a specific wakelock. Once the countdown timer finishes, i want to display an alert dialog. When the wakelock is released, the timer should be killed. When the timer is running and I get a user activity, I want to kill the previous timer and start a new one (or maybe reset this one)
What would be the best way for me to implement this?
Yes you can do it, the only thing you need to do is like this -->
`val dialogListener = DialogInterface.OnClickListener { dialog, which ->
when (which) {
BUTTON_POSITIVE -> {
}
DialogInterface.BUTTON_NEGATIVE -> {
}
}
}
val dialog = AlertDialog.Builder(this)
.setTitle(“YOUR TITLE HERE”)
.setMessage(“YOUR MESSAGE HERE)
.setPositiveButton(“TEXT OF BUTTON”)
.setNegativeButton(“TEXT OF BUTTON”)
.create()
dialog.setOnShowListener(object : OnShowListener {
private val AUTO_DISMISS_MILLIS = 5000 //Timer in this case 5 Seconds
override fun onShow(dialog: DialogInterface) {
//if you want to have stuff done by your buttons it's going go be here with a respective call like (dialog as AlertDialog).getButton(The button you want positive or negative)
then everything you want to happen on it
*************************************************
//here is the timer associated to the button
val defaultButton: Button =
dialog.getButton(AlertDialog.BUTTON_POSITIVE)
val positiveButtonText: CharSequence = defaultButton.text
object : CountDownTimer(AUTO_DISMISS_MILLIS.toLong(), 100) {
override fun onTick(millisUntilFinished: Long) {
defaultButton.text = java.lang.String.format(
Locale.getDefault(), "%s (%d)",
positiveButtonText,
TimeUnit.MILLISECONDS.toSeconds(millisUntilFinished) + 1
)
}
override fun onFinish() {
//everything you wanna do on the moment the timer is off is going to happen here if you wanna open another dialog you need to call it here
}
}.start()
}
})
dialog.show()
}
`
Related
There is a task: the application has a CountDownTimer. I need to make sure that the user sees a message (for example, it could be Toast messsage) when the timer expires. The timer depends on real time, so even if the user does not use the application, the timer still runs. However, there is a condition - if the timer ended exactly when the user was on the fragment, then this message is shown to him. If the user has entered the application after the timer has expired, the message will not be shown to him. How can this be implemented?
There are other ways to do it, but this would be my approach.
When you start the timer, persist the end time (such as storing it in SharedPreferences).
When your fragment comes on screen, or when you start the timer, use this persisted time to determine if there is a future timer end time. If there is, start counting down with a CountDownTimer or coroutine, cancelling any previous timer that might have been set.
Cancel the timer whenever the fragment goes off screen.
I would typically persist the end time in a ViewModel, but to keep this example simple, I'll just do it in the Fragment.
private const TIMER_END_TIME_KEY = "timerEndTime"
class MyFragment: Fragment() {
// ...
private var countDownJob: Job? = null
// Only safe while fragment attached
private val sharedPreferences: SharedPreferences
get() = PreferenceManager.getDefaultSharedPreferences(requireContext())
private fun startNewCountDown(durationMillis: Long) {
val endTime = System.currentTimeMillis() + durationMillis
sharedPreferences.edit {
putLong(TIMER_END_TIME_KEY, endTime)
}
prepareCountDownCallback()
}
private fun cancelCountDown() {
countDownJob?.cancel()
countDownJob = null
sharedPreferences.edit {
putLong(TIMER_END_TIME_KEY, 0L)
}
}
private fun prepareCountDownCallback() {
countDownJob?.cancel()
val now = System.currentTimeMillis()
val endTime = sharedPreferences.getLong(TIMER_END_TIME_KEY, 0L)
countDownJob = if (endTime > now) {
viewLifecycleOwner.lifecycleScope.launch {
delay(endTime - now)
// Show end of timer message
}
} else {
null
}
}
override fun onPause() {
super.onPause()
countDownJob?.cancel()
countDownJob = null
}
override fun onResume() {
super.onResume()
prepareCountDownCallback()
}
}
I use this fragment of code in an activity to display a timer countdown:
//SET COUNTDOWN TIMER in onCreate()
//var tempo is a :Long number
var textView = findViewById<TextView>(R.id.countdownTimer)
object : CountDownTimer(tempo, 1000) {
override fun onTick(millisUntilFinished: Long) {
textView.setText("Remaining time: "+tim(millisUntilFinished) )
}
override fun onFinish() {
textView.text = "Times up!"
}
}.start()
If possible I'd like to implement the possibility to pause this CountDownTimer in onBackPressed() and resume it again?
override fun onBackPressed() {
AlertDialog.Builder(this).apply {
setTitle("Confirm")
setMessage("Exit app?")
setPositiveButton("Yes") { _, _ ->
// if user press yes, cancel timer?
super.onBackPressed()
}
setNegativeButton("No"){_, _ ->
// if user press no, then return the activity and resume timer?
}
setCancelable(true)
}.create().show()
}
My idea would be to edit var tempo differentitate it everytime OnBackPressed and copy the section of this 1st code piece in onCreate() and put it onResume().
I'm quite confused on using this tbh. MAybe there's a faster and better way?
You cannot pause CountDownTimer. What you can do is stop it and then start another one (with the remaining time) when you want to resume it.
I have a button in my activity. When the user clicks on the button, progress is shown and a request is sent to the server, after receiving a response to the request, the progress is hided and the next activity is opened.
However, if the user has time to press the button several times, two or more activities will open.
For preventing double click I use DebouncedOnClickListener. Here is my code:
abstract class DebouncedOnClickListener protected constructor() : View.OnClickListener {
private val minimumIntervalMillis: Long = 1000
private val lastClickMap: MutableMap<View, Long>
abstract fun onDebouncedClick(v: View?)
override fun onClick(clickedView: View) {
val previousClickTimestamp = lastClickMap[clickedView]
val currentTimestamp = SystemClock.uptimeMillis()
lastClickMap[clickedView] = currentTimestamp
if (previousClickTimestamp == null || abs(currentTimestamp - previousClickTimestamp) > minimumIntervalMillis) {
onDebouncedClick(clickedView)
}
}
init {
lastClickMap = WeakHashMap()
}
}
This method works in many cases. However, the request can be processed for an unknown amount of time. And the user can click on the button while the request is completed, the progress will be closed and the process of opening the next activity will start. I do not know how long a new activity can be launched, and at this moment another click on the button can occur, which will subsequently lead to the opening of two activities.
How can you avoid double clicking until the activity opens, please help me.
P.S. Even when I try to disable the button, double-clicking can still happen during the launch of the activity
Just paste this on button click
button.setEnabled(false);
You can use a boolean flag like following.
private val onClicked: Boolean = false
override fun onClick(clickedView: View) {
if(!onClicked) {
onClicked = true
// Do something
onClicked = false
}
}
Add required functions in Utils and wherever it's required call it by passing view as parameter.
const val DIGIT_THOUSAND = 1000
fun preventMultipleTap(view: View) {
preventMultipleTap(view, DIGIT_THOUSAND.toLong())
}
fun preventMultipleTap(view: View, delayInMillis: Long) {
view.isEnabled = false
view.postDelayed({ view.isEnabled = true }, delayInMillis)
}
I have an AlertDialog that I want to display at least once to the user and then continuously display the dialog to the user even after the user clicks "ok" until a certain condition is met.
Here's the code structure I have so far for the AlertDialog:
do {
val dialogShow: AlertDialog.Builder = AlertDialog.Builder(this#MainActivity)
dialogShow.setCancelable(false)
dialogShow.setMessage("Message")
.setPositiveButton(
"ok",
object : DialogInterface.OnClickListener {
override fun onClick(dialogInterface: DialogInterface, i: Int) {
if (checkCondition()) {
conditionMet = true
} else {
// Keep looping
}
}
})
.setNegativeButton(
"cancel",
object : DialogInterface.OnClickListener {
override fun onClick(dialogInterface: DialogInterface, i: Int) {
conditionMet = true
return
}
})
dialogShow.show()
} while (conditionMet == false)
The problem now that I am facing is the AlertDialog will display once, but then never again. Even if conditionMet = false it still won't continue to display. How do I keep displaying the same AlertDialog in a loop?
By wrapping the show code in a loop, you're showing it continuously. What you probably want to do it re-show the dialog if it is dismissed. So something like this pseudocode:
fun showObtrusiveDialog() {
...
dialog.setPositiveButton {
if(shouldStillBeObtrusive()) showObtrusiveDialog()
...
}.setNegativeButton {
...
}
dialog.show()
}
An alternate way to handle this would be to disable the buttons until you're ready to allow the dialog to be closed by the user. Here's an extension function you could call when your condition changes:
fun AlertDialog.setAllButtonsState(enabled: Boolean) {
arrayOf(DialogInterface.BUTTON_POSITIVE, DialogInterface.BUTTON_NEGATIVE, DialogInterface.BUTTON_NEUTRAL)
.forEach { getButton(it)?.setEnabled(enabled) }
}
So you can call this to disabled them before you show it, and call it again when your condition changes. You'll need to keep the dialog in a property so you can access it from wherever your condition is being changed.
I want to show a progress dialog and dismiss it after the onCompleteListener has responded as follows:
class DialogSubjectsAdd: DialogFragment() {
private val db = FirebaseFirestore.getInstance().collection("courses")
private var docFetched = false
private var processCompleted = false
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
super.onCreateDialog(savedInstanceState)
getCoursesIndexDoc()
// show progress dialog
// wait until download operation is completed
while (!processCompleted) {}
// dismiss dialog
// todo
}
private fun getCoursesIndexDoc() {
// fetch the index document
db.document("all")
.get()
.addOnCompleteListener { task ->
if (task.isSuccessful) {
docFetched = true
}
processCompleted = true
}
}
}
But the above code freezes the app.
If I comment the while loop and dismiss dialog code as:
// while (!processCompleted) {}
// // dismiss dialog
the progress dialog shows forever.
So, why is the while loop freezing the app?
Even if the value of processCompleted never becomes true, I think it should result in progress bar being running forever rather than freezing the app.
But even the progress dialog doesn't show up because of while loop and the button which shows the dialog remains clicked and the app gets freezed, why?
That's because onCreateDialog runs on the system's UI thread - meaning the UI can't update while something is running.
The solution is to move the code to dismiss the dialog to a separate thread - your completion listener seems like the perfect place!
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
super.onCreateDialog(savedInstanceState)
getCoursesIndexDoc()
// Don't do anything else here!
}
private fun getCoursesIndexDoc() {
// fetch the index document
db.document("all")
.get()
.addOnCompleteListener { task ->
if (task.isSuccessful) {
docFetched = true
}
// Instead of setting the flag, dismiss the dialog here
}
}