Reputation: 117
I'm working on a QR reader app for Android. The main point of the app is to read QR codes using the phone camera. If the user is not working with the app the camera gets stopped after a certain time (using a Timer
for this) to prevent battery consumption and making vibrate the phone, displaying a toast with a message indicating the user to push a button to reactivate the camera and then continue with his job.
The problem is that when the event gets fired for the first time everything works as I was expecting but if it gets fired for a second time the Toast will be displayed twice, and so on if the event gets fired for an N number of times.
The logic behind this workaround is here:
Button logic that "resumes" the camera functionallity:
btnEncenderCamara.Click += (sender, e) => {
camara.Start(lectorQR.Holder);
btnEncenderCamara.Enabled = false;
timerToDisableCamera.Interval = 6000;
timerToDisableCamera.Elapsed += new ElapsedEventHandler(timerElapsed);
timerToDisableCamera.Start();
};
and the timer's event logic here:
protected void timerElapsed(object sender, ElapsedEventArgs e) {
try
{
timerToDisableCamera.Stop();
RunOnUiThread(() => {
Vibrator vibrator = (Vibrator)GetSystemService(Context.VibratorService);
vibrator.Vibrate(1000);
camara.Stop();
btnEncenderCamara.Enabled = true;
Toast.MakeText(this, "La cámara se ha detenido para ahorrar en cosumo de batería. Presione 'ENCENDER CÁMARA' para encender la cámara nuevamente", ToastLength.Short).Show();
});
}
catch (Exception ex)
{
throw;
}
}
I need to find how I can avoid the Toast to get displayed more than once, once the event that stops the camera gets fired.
Upvotes: 2
Views: 129
Reputation: 1553
Notice how in your btnEncenderCamara.Click event, you are subscribing a new delegate to the timerToDisableCamera.Elapsed event, every time btnEncenderCamara.Click is triggered.
Therefore, with every subsequent call to btnEncenderCamara.Click, you timerElapsed method will be called more than once.
A way to fix this would be to unsubscribe your delegate inside the timerElapsed method, after your call to timerToDisableCamera.Stop(), like this:
protected void timerElapsed(object sender, ElapsedEventArgs e) {
try
{
timerToDisableCamera.Stop();
timerToDisableCamera.Elapsed -= new ElapsedEventHandler(timerElapsed);
RunOnUiThread(() => {
Vibrator vibrator = (Vibrator)GetSystemService(Context.VibratorService);
vibrator.Vibrate(1000);
camara.Stop();
btnEncenderCamara.Enabled = true;
Toast.MakeText(this, "La cámara se ha detenido para ahorrar en cosumo de batería. Presione 'ENCENDER CÁMARA' para encender la cámara nuevamente", ToastLength.Short).Show();
});
}
catch (Exception ex)
{
throw;
}
}
Alternatively, you could refactor your code to initialize the timer just once, which will keep your logic lean, something like this:
// I'm assuming this method is an entry point for initialization in your Android activity.
void OnLoad()
{
// I'm assuming timerToDisableCamera already have an instance of Timer, otherwise you will get a NullReferenceException.
timerToDisableCamera.Interval = 6000;
timerToDisableCamera.Elapsed += new ElapsedEventHandler(timerElapsed);
// And also assuming that btnEncenderCamara is already an instance of Button.
btnEncenderCamara.Click += (sender, e) => {
camara.Start(lectorQR.Holder);
btnEncenderCamara.Enabled = false;
timerToDisableCamera.Start();
};
}
protected void timerElapsed(object sender, ElapsedEventArgs e)
{
try
{
timerToDisableCamera.Stop();
RunOnUiThread(() => {
Vibrator vibrator = (Vibrator)GetSystemService(Context.VibratorService);
vibrator.Vibrate(1000);
camara.Stop();
btnEncenderCamara.Enabled = true;
Toast.MakeText(this, "La cámara se ha detenido para ahorrar en cosumo de batería. Presione 'ENCENDER CÁMARA' para encender la cámara nuevamente", ToastLength.Short).Show();
});
}
catch (Exception ex)
{
throw;
}
}
Upvotes: 1