I have a CounterTimer which starts when an Api is successful. if a user presses the KeyEvent.KEYCODE_HOME CounterTimer must cancel but instead of cancelling it keeps running
This is how i start the counter
private void countDownStart(String event_date_time) {
String[] tokens = event_date_time.split(":");
int secondsToMs = Integer.parseInt(tokens[2]) * 1000;
int minutesToMs = Integer.parseInt(tokens[1]) * 60000;
int hoursToMs = Integer.parseInt(tokens[0]) * 3600000;
long total = secondsToMs + minutesToMs + hoursToMs;
timer = new CountDownTimer(total, 1000) {
public void onTick(long millisUntilFinished) {
int seconds = (int) (millisUntilFinished / 1000) % 60;
int minutes = (int) ((millisUntilFinished / (1000 * 60)) % 60);
int hours = (int) ((millisUntilFinished / (1000 * 60 * 60)) % 24);
CountDown.setVisibility(View.VISIBLE);
CountDown.setText(String.format("%02d:%02d:%02d", hours, minutes, seconds));
}
public void onFinish() {
CountDown.setVisibility(View.VISIBLE);
CountDown.setText("00:00:00");
loader.showProgressBar();
startActivity(new Intent(mContext, Questions.class));
}
};
timer.start();
}
and this is what I used to cancel. I've tried with both onStop and KeyEvent.KEYCODE_HOME
#Override
protected void onStop() {
System.out.println("count: onStop Called");
loader.hideProgressBar();
if (timer != null) {
timer.cancel();
}
super.onStop();
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_HOME)) {
loader.hideProgressBar();
System.out.println("KEYCODE_HOME");
if (timer != null) {
timer.cancel();
}
return true;
}
return false;
}
What could be possibly wrong here?
Related
I have a time counter bug in the game, how to fix it?
In my game, when there are 6 seconds left, the timer turns red and the animation is played, but sometimes the code freezes :(
It is this code that is bugging me, how to implement it differently:
if (mTimeLeftInMillis == 6000){
mTextViewCountDown.setTextColor(Color.RED);
mTextViewCountDown.startAnimation(zooming_second);
soundPlay(watch);
}
Here is the detailed code:
private void startTimer() {
mCountDownTimer = new CountDownTimer(mTimeLeftInMillis, 1000) {
#Override
public void onTick(long millisUntilFinished) {
mTimeLeftInMillis = millisUntilFinished;
if (mTimeLeftInMillis == 6000){
mTextViewCountDown.setTextColor(Color.RED);
mTextViewCountDown.startAnimation(zooming_second);
soundPlay(watch);
}
int minutes = (int) (mTimeLeftInMillis / 1000) / 60;
int seconds = (int) (mTimeLeftInMillis / 1000) % 60;
String timeLeftFormatted = String.format(Locale.ENGLISH, "%02d:%02d", minutes, seconds);
mTextViewCountDown.setText(timeLeftFormatted);
mTimerRunning = true;
}
#Override
public void onFinish() {
mTimerRunning = false;
TimeOutDialog();
soundPlay(time_out_sound);
mediaPlayer1.stop();
tadam_false.stop();
tadam_true.stop();
watch.stop();
mTextViewCountDown.clearAnimation();
}
}.start();
}
How to make this action happen at the end of the time when there are still 5 seconds left.
This is the code of the action that I want to do when there are 5 seconds left from the timer:
mTextViewCountDown.setTextColor(Color.RED);
zooming_second = AnimationUtils.loadAnimation(Level1.this, R.anim.watch_zoo);
mTextViewCountDown.startAnimation(zooming_second);
soundPlay(watch);
In this method:
//variable start
private static final long START_TIME_IN_MILLIS = 60000;
private TextView mTextViewCountDown;
private CountDownTimer mCountDownTimer;
private boolean mTimerRunning;
private long mTimeLeftInMillis = START_TIME_IN_MILLIS;
//variable end
private void startTimer() {
mCountDownTimer = new CountDownTimer(mTimeLeftInMillis, 1000) {
#Override
public void onTick(long millisUntilFinished) {
mTimeLeftInMillis = millisUntilFinished;
int minutes = (int) (mTimeLeftInMillis / 1000) / 60;
int seconds = (int) (mTimeLeftInMillis / 1000) % 60;
String timeLeftFormatted = String.format(Locale.ENGLISH, "%02d:%02d", minutes, seconds);
mTextViewCountDown.setText(timeLeftFormatted);
mTimerRunning = true;
}
#Override
public void onFinish() {
mTimerRunning = false;
TimeOutDialog();
soundPlay(time_out_sound);
mediaPlayer1.stop();
tadam_false.stop();
tadam_true.stop();
}
}.start();
}
private void pauseTimer () {
mCountDownTimer.cancel();
mTimerRunning = false;
}
Inside onTick(long) method simply check how many seconds are left. And if it is five, trigger your action.
EDIT
My approach to hitting the right time would be something like this:
if (Math.abs(mTimeLeftInMillis - 5000) < 100) {
// start the animation
}
This would avoid issues with if (mTimeLeftInMillis == 5000) as it gives it some buffer. It's needed because it will not trigger at exactly 5000 milliseconds.
Thanks! I did it like this:
if (mTimeLeftInMillis <= 6000){
mTextViewCountDown.setTextColor(Color.RED);
zooming_second = AnimationUtils.loadAnimation(Level1.this,
R.anim.watch_zoo);
mTextViewCountDown.startAnimation(zooming_second);
soundPlay(watch);
}
This method is of a counter and the if statements have a value I want to pass to another method. The value is called stageTime
private void startTimer() {
Log.println(Log.ASSERT, "CHECK","Entered startTimer() method");
millisInFuture = mTimeLeftInMillis;
mCountDownTimer = new CountDownTimer(mTimeLeftInMillis, 1000) {
#Override
public void onTick(long millisUntilFinished) {
mTimeLeftInMillis = millisUntilFinished;
updateCountDownText();
final long millisPassed = millisInFuture - mTimeLeftInMillis;
progress = (int) (millisPassed * 100 / millisInFuture);
pb.setProgress(progress);
pb2.setProgress(0);
pb3.setProgress(0);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Key: 60 sec
if (millisInFuture == 480000) {
if (millisPassed <= 60000 || (millisPassed > 180000 && millisPassed <= 240000) || (millisPassed > 300000 && millisPassed <= 360000 || (millisPassed > 420000 && millisPassed <= 480000))) {
Animation animation = AnimationUtils.loadAnimation(tool1mode1.this, R.anim.fade_in);
stepupimage.setAnimation(animation);
stage_timer2.setVisibility(View.INVISIBLE);
stage_timer.setVisibility(View.VISIBLE);
pb3.setProgress(0);
stageTime = 60000;
Log.println( Log.VERBOSE,"CHECK","StageTime= "+stageTime);
} else if ((millisPassed > 60000 && millisPassed <= 180000)) {
Animation animation = AnimationUtils.loadAnimation(tool1mode1.this, R.anim.fade_in);
stepdownimage.setAnimation(animation);
stage_timer.setVisibility(View.INVISIBLE);
stage_timer2.setVisibility(View.VISIBLE);
pb2.setProgress(0);
stageTime = 120000;
// stageDown((int) timeleft[5]);
Log.println( Log.VERBOSE,"CHECK","StageTime= "+stageTime);
} else if ((millisPassed > 240000 && millisPassed <= 300000) || (millisPassed > 360000 && millisPassed <= 420000)){
Animation animation = AnimationUtils.loadAnimation(tool1mode1.this, R.anim.fade_in);
stepdownimage.setAnimation(animation);
stage_timer.setVisibility(View.INVISIBLE);
stage_timer2.setVisibility(View.VISIBLE);
pb2.setProgress(0);
// stageDown((int) timeleft[3]);
stageTime = 60000;
}
}
This above is part of my countdown timer.
Now I want to pass stageTime from the above method to this method:
private void stageUp(int value)
{
mTimeLeftInMillis2 = value ;
Log.println(Log.VERBOSE, "Check", "stage_time = "+mTimeLeftInMillis2);
millisInFuture2 = mTimeLeftInMillis2;
CountDownTimer cdt = new CountDownTimer(mTimeLeftInMillis2, 1000) {
#Override
public void onTick(long millisUntilFinished) {
Log.println(Log.ASSERT, "CHECK2", "stageup entered");
mTimeLeftInMillis2 = millisUntilFinished;
updateStageUpCount();
long timestamp = millisInFuture2 - mTimeLeftInMillis2;
progress2 = (int) (timestamp*100 /millisInFuture2);
pb2.setProgress(progress2);
}
#Override
public void onFinish() {
}
}.start();
}
This is another countdown timer but the value I'll pass would determine how long it would run.
Now when it's time for action the second method is never called:
newbtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startTimer();
if(stageTime != -1){
Log.println(Log.ASSERT, "CHECK","Entered stageUp method");
stageUp(stageTime);
}
}
});
But when I manually add a value for the stageUp method it works any idea how to solve this problem?
My app has a RecyclerView in which I will display an audio player.I want to show the current audio time as the audio is played, but when playing play android gives the following error:
E/AndroidRuntime: FATAL EXCEPTION: Thread-4
Process: myapp.myapp.com.myapp, PID: 24800
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:7154)
at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:1128)
at android.view.ViewGroup.invalidateChild(ViewGroup.java:5286)
at android.view.View.invalidateInternal(View.java:13616)
at android.view.View.invalidate(View.java:13580)
at android.view.View.invalidate(View.java:13564)
at android.widget.TextView.checkForRelayout(TextView.java:7616)
at android.widget.TextView.setText(TextView.java:4730)
at android.widget.TextView.setText(TextView.java:4587)
at android.widget.TextView.setText(TextView.java:4542)
at myapp.myapp.com.myapp.adapter.MyAdapter$12$1.run(MyAdapter.java:882)
at java.lang.Thread.run(Thread.java:761)
Methods inside onBindViewHolder:
private void reproduzirAudio(MyViewHolder h, File arquivoAudio) {
h.playAudioChat.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Criar player
if (h.mediaPlayer == null) {
h.mediaPlayer = MediaPlayer.create(context, Uri.fromFile(arquivoAudio));
h.mediaPlayer.setLooping(false);
h.mediaPlayer.seekTo(0);
//Configurar playButton
if (!h.mediaPlayer.isPlaying()) {
//stopping
h.mediaPlayer.start();
h.playAudioChat.setImageResource(R.drawable.ic_pause_circle);
} else {
h.mediaPlayer.pause();
h.playAudioChat.setImageResource(R.drawable.ic_play_circle_white);
}
//Audiowave
h.audioThread = new Thread(new Runnable() {
#Override
public void run() {
while (h.mediaPlayer != null){
if (h.mediaPlayer.isPlaying()) {
try {
//Audiowave
float waveProgress = h.mediaPlayer.getCurrentPosition()
/ (float) h.mediaPlayer.getDuration() * 100F;
h.audioWaveView.setProgress(waveProgress);
Log.i("WAVE",String.valueOf(waveProgress));
//Timer
h.timeAudioChat.setText(formatarTempoAudio(h.mediaPlayer.getCurrentPosition()));
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
});
h.audioThread.start();
//...
}
}
});
}
private String formatarTempoAudio(long currentPosition) {
String finalTimerString = "";
String secondsString = "";
int hours = (((int) currentPosition / (1000 * 60 * 60)));
int minutes = ((int) currentPosition % (1000 * 60 * 60)) / (1000 * 60);
int seconds = (((int) currentPosition % (1000 * 60 * 60)) % (1000 * 60) / 1000);
// Add hours if there
if (hours > 0) {
finalTimerString = hours + ":";
}
if (seconds < 10) {
secondsString = "0" + seconds;
} else {
secondsString = "" + seconds;
}
finalTimerString = finalTimerString + minutes + ":" + secondsString;
return finalTimerString;
}
Error runs on line
h.timeAudioChat.setText (formatTempoAudio(h.mediaPlayer.getCurrentPosition ()));
I believe this update should be done in the UI Thread, but since I'm on a RecyclerViewAdapter I can't access it. How can I update TextView with the current audio time?
Thanks in advance!
Despite the organization of your code, which is way improvable, as you have a reference of a context inside the ViewHolder parameter, you can run inside the UI thread this way:
Handler handlerInMainThread = new Handler(itemView.getContext().getMainLooper());
Runnable yourRunnable = new Runnable() {
#Override
public void run() {
// Put the desired UI code here
}
}
handlerInMainThread.post(yourRunnable);
This is a nice answer with a similar problem, and this covers different ways of accessing to UI. There is also documentation in the android docs.
I'm trying to implement a simple fall detection algorithm using android's accelerometer.
The current acceleration of the phone is stored in mAccel and if a sudden shake is detected Timer t begins. Inside the Timer are two CountDownTimers.
The firstTimer is used to register a "fall" if the user hasn't moved in the last 5 seconds and the secondTimer to confirm that fall if the user hasn't pressed a button (not implemented yet).
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
mGravity = event.values.clone();
float x = mGravity[0];
float y = mGravity[1];
float z = mGravity[2];
mAccelLast = mAccelCurrent;
mAccelCurrent = (float) Math.sqrt(x * x + y * y + z * z);
float delta = mAccelCurrent - mAccelLast;
mAccel = mAccel * 0.9f + delta;
mAccel = abs(mAccel);
textView.setText("a:"+mAccel);
if (abs(mAccel) > 5.0f) { // Shake detection
t();
}
}
}
public void t () {
firstTimer = new CountDownTimer(5000, 1000) { //fall registration timer
#Override
public void onTick(long millisUntilFinished) {
//if there is movement before 5 seconds pass cancel the timer
if (abs(mAccel) > 2.0f) {
firstTimer.cancel();
firstTimer = null;
}
}
#Override
public void onFinish() {
toast.show();
secondTimer = new CountDownTimer(30*1000, 1000) { //fall confirmation timer
#Override
public void onTick(long millisUntilFinished) {
textView2.setText("" + millisUntilFinished / 1000);
}
#Override
public void onFinish() {
Toast toast = Toast.makeText(getApplicationContext(),
"Fall Registered!", Toast.LENGTH_LONG);
toast.show();
}
}.start();
}
}.start();
}
The main issue is that most timesfirstTimer is accessed, it gets cancelled due to the mAccel being over the 2.0 threshold, while it is decelerating.
I tried to use Timer.schedule() to delay the activation of firstTimer until after the acceleration value has "rested" but it won't work with it.
I created a new Timer object mTimer and this seems to work on every case.
CountDownTimer firstTimer = new CountDownTimer(5000, 1000) { //fall registration timer
#Override
public void onTick(long millisUntilFinished) {
//if there is movement before 5 seconds pass cancel the timer
if (abs(mAccel) > 2.0f) {
Toast toast = Toast.makeText(getApplicationContext(),
"Fall not Detected", Toast.LENGTH_SHORT);
toast.show();
firstTimer.cancel();
}
}
#Override
public void onFinish() {
Toast toast = Toast.makeText(getApplicationContext(),
"Fall Detected!", Toast.LENGTH_SHORT);
toast.show();
secondTimer.start();
}
};
CountDownTimer secondTimer = new CountDownTimer(30*1000, 1000) {
//fall confirmation timer
#Override
public void onTick(long millisUntilFinished) {
textView2.setText("" + millisUntilFinished / 1000);
}
#Override
public void onFinish() {
Toast toast = Toast.makeText(getApplicationContext(),
"Fall Registered!", Toast.LENGTH_LONG);
toast.show();
}
};
#Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
mGravity = event.values.clone();
float x = mGravity[0];
float y = mGravity[1];
float z = mGravity[2];
mAccelLast = mAccelCurrent;
mAccelCurrent = (float) Math.sqrt(x * x + y * y + z * z);
float delta = mAccelCurrent - mAccelLast;
mAccel = mAccel * 0.9f + delta;
mAccel = abs(mAccel);
textView.setText("a:"+mAccel);
if (abs(mAccel) > 5.0f) { // Shake detection
mTimer.schedule(new TimerTask() {
//start after 2 second delay to make acceleration values "rest"
#Override
public void run() {
firstTimer.start();
}
}, 2000);
}
}
}