I'm working on a fragment that includes two timers that take user input. Timer B is linked to timer A such that when timer A is started, so is timer B. The purpose of timer A is to count down once. The purpose of timer B is to count down, beep once at 0, reset to the original value, and repeat. The problem I'm having is that when timer B reaches 0, it doesn't reset and start counting down right away. The timer seems to be delayed further by calling ToneGenerator. This results in the two timers becoming more and more out of sync each time timer B resets.
For example, say timer A is set to 10 seconds and timer B is set to 2 seconds.
-The timers are started-
timer A:10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
timer B: 2, 1, 0, 2, 1, 0, 2, 1, 0, 2, 1
The timer only beeps 3 times (when timer B is at 0,) when it should beep 5 times (5 sets of 2 second intervals = 10 seconds: the value of timer A.) In other words, I'd like timer B to beep when timer A is at 8, 6, 4, 2, and 0.
How might I accomplish this?
I've consulted the forums, YT, and have tried playing around with the value for millisInFuture.
Fragment
public class fragmentdro extends Fragment {
private EditText session_edit_text;
private EditText dro_edit_text;
private TextView session_text_view;
private TextView dro_text_view;
private Button session_start_button;
private Button dro_start_button;
private Button session_reset_button;
private Button dro_reset_button;
private Button session_set_button;
private Button dro_set_button;
private CountDownTimer sessionTimer;
private CountDownTimer droTimer;
private boolean SessionTimerRunning;
private boolean DROTimerRunning;
private long SessionStartTimeInMillis;
private long DROStartTimeInMillis;
private long SessionTimeLeftInMillis = SessionStartTimeInMillis;
private long DROTimeLeftInMillis = DROStartTimeInMillis;
private long SessionEndTime;
private long DROEndTime;
View View;
public View onCreateView(#NonNull LayoutInflater inflater, #NonNull ViewGroup container, #NonNull Bundle savedInstanceState) {
View = inflater.inflate(R.layout.dro_fragment, container, false);
session_edit_text = View.findViewById(R.id.session_edit_text);
dro_edit_text = View.findViewById(R.id.dro_edit_text);
session_text_view = View.findViewById(R.id.session_text_view);
dro_text_view = View.findViewById(R.id.dro_text_view);
session_start_button = View.findViewById(R.id.session_start_button);
dro_start_button = View.findViewById(R.id.dro_start_button);
session_reset_button = View.findViewById(R.id.session_reset_button);
dro_reset_button = View.findViewById(R.id.dro_reset_button);
session_set_button = View.findViewById(R.id.session_set_button);
dro_set_button = View.findViewById(R.id.dro_set_button);
InputMethodManager imm = (InputMethodManager)getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
ToneGenerator toneGenerator = new ToneGenerator(AudioManager.STREAM_ALARM, 100);
Timer A
session_set_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(android.view.View view) {
String Sessioninput = session_edit_text.getText().toString();
if (Sessioninput.length() == 0) {
Toast.makeText(getActivity(), "Fill it in", Toast.LENGTH_SHORT).show();
return;
}
long millisInput = Long.parseLong(Sessioninput) * 1000;
if (millisInput == 0) {
Toast.makeText(getActivity(), "Please enter a positive number", Toast.LENGTH_SHORT).show();
return;
}
setSessionTime(millisInput);
session_edit_text.setText("");
imm.hideSoftInputFromWindow(getView().getWindowToken(), 0);
}
});
session_start_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(android.view.View view) {
if (SessionTimerRunning) {
pauseSessionTimer();
resetDROTimer();
} else {
startSessionTimer();
startDROTimer();
}
}
});
session_reset_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(android.view.View view) {
if (SessionTimerRunning) {
resetSessionTimer();
} else {
resetSessionTimer();
}
}
});
public void setSessionTime(long milliseconds) {
SessionStartTimeInMillis = milliseconds;
resetSessionTimer();
}
public void startSessionTimer() {
SessionEndTime = System.currentTimeMillis() + SessionTimeLeftInMillis;
sessionTimer = new CountDownTimer(SessionTimeLeftInMillis, 100) {
#Override
public void onTick(long millisUntilFinished) {
SessionTimeLeftInMillis = millisUntilFinished;
updateSessionText();
}
#Override
public void onFinish() {
SessionTimerRunning = false;
session_start_button.setText("Start");
session_start_button.setVisibility(android.view.View.INVISIBLE);
session_reset_button.setVisibility(android.view.View.VISIBLE);
}
}.start();
SessionTimerRunning = true;
session_start_button.setText("Pause");
}
public void pauseSessionTimer() {
sessionTimer.cancel();
droTimer.cancel();
SessionTimerRunning = false;
DROTimerRunning = false;
session_start_button.setText("Start");
updateSessionText();
}
public void resetSessionTimer() {
if (SessionTimerRunning) {
sessionTimer.cancel();
SessionTimeLeftInMillis = (SessionStartTimeInMillis);
updateSessionInterface();
startSessionTimer();
} else {
SessionTimeLeftInMillis = (SessionStartTimeInMillis);
updateSessionText();
updateSessionInterface();
session_reset_button.setVisibility(android.view.View.VISIBLE);
session_start_button.setVisibility(android.view.View.VISIBLE);
}
}
public void updateSessionText() {
int minutes = (int) (SessionTimeLeftInMillis/1000/60);
int seconds = (int) (SessionTimeLeftInMillis/1000)%60;
String timeLeftFormatted = String.format(Locale.getDefault(),
timeLeftFormatted = String.format(Locale.getDefault(), "%2d:%02d", minutes, seconds));
session_text_view.setText(timeLeftFormatted);
}
public void updateSessionInterface() {
if (SessionTimerRunning) {
session_edit_text.setVisibility(android.view.View.INVISIBLE);
session_set_button.setVisibility(android.view.View.INVISIBLE);
session_reset_button.setVisibility(android.view.View.VISIBLE);
session_start_button.setText("Pause");
} else {
session_edit_text.setVisibility(android.view.View.VISIBLE);
session_set_button.setVisibility(android.view.View.VISIBLE);
session_reset_button.setVisibility(android.view.View.VISIBLE);
session_start_button.setText("Start");
}
}
Timer B
dro_set_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(android.view.View view) {
String DROinput = dro_edit_text.getText().toString();
if (DROinput.length() == 0) {
Toast.makeText(getActivity(), "Fill it in", Toast.LENGTH_SHORT).show();
return;
}
long millisInput = Long.parseLong(DROinput) * 1000;
if (millisInput == 0) {
Toast.makeText(getActivity(), "Please enter a positive number", Toast.LENGTH_SHORT).show();
return;
}
setDROTime(millisInput);
dro_edit_text.setText("");
imm.hideSoftInputFromWindow(getView().getWindowToken(), 0);
}
});
dro_start_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(android.view.View view) {
if (DROTimerRunning) {
pauseDROTimer();
} else {
startDROTimer();
}
}
});
dro_reset_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(android.view.View view) {
if (DROTimerRunning) {
resetDROTimer();
} else {
resetDROTimer();
}
}
});
return View;
}
public void setDROTime(long milliseconds) {
DROStartTimeInMillis = milliseconds;
resetDROTimer();
}
public void startDROTimer() {
DROEndTime = System.currentTimeMillis() + DROTimeLeftInMillis;
droTimer = new CountDownTimer(DROTimeLeftInMillis, 100) {
#Override
public void onTick(long millisUntilFinished) {
DROTimeLeftInMillis = millisUntilFinished;
updateDROText();
}
#Override
public void onFinish() {
ToneGenerator toneGenerator = new ToneGenerator(AudioManager.STREAM_ALARM, 400);
toneGenerator.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 200);
DROTimerRunning = false;
DROTimeLeftInMillis = DROStartTimeInMillis;
updateDROText();
updateDROInterface();
startDROTimer();
}
}.start();
DROTimerRunning = true;
dro_start_button.setText("Pause");
}
public void pauseDROTimer() {
droTimer.cancel();
DROTimerRunning = false;
dro_start_button.setText("Start");
updateDROText();
}
public void resetDROTimer() {
if (DROTimerRunning) {
droTimer.cancel();
DROTimeLeftInMillis = (DROStartTimeInMillis);
updateDROInterface();
startDROTimer();
} else {
DROTimeLeftInMillis = (DROStartTimeInMillis);
updateDROText();
updateDROInterface();
dro_reset_button.setVisibility(android.view.View.VISIBLE);
dro_start_button.setVisibility(android.view.View.VISIBLE);
}
}
public void updateDROText() {
int seconds = (int) (DROTimeLeftInMillis/1000)%60;
String timeLeftFormatted = String.format(Locale.getDefault(),
timeLeftFormatted = String.format(Locale.getDefault(), ":%02d", seconds));
dro_text_view.setText(timeLeftFormatted);
}
public void updateDROInterface() {
if (DROTimerRunning) {
dro_edit_text.setVisibility(android.view.View.INVISIBLE);
dro_set_button.setVisibility(android.view.View.INVISIBLE);
dro_reset_button.setVisibility(android.view.View.VISIBLE);
dro_start_button.setText("Pause");
} else {
dro_edit_text.setVisibility(android.view.View.VISIBLE);
dro_set_button.setVisibility(android.view.View.VISIBLE);
dro_reset_button.setVisibility(android.view.View.VISIBLE);
dro_start_button.setText("Start");
}
}
#Override
public void onStop() {
super.onStop();
SharedPreferences preferences = this.getActivity().getSharedPreferences("prefs", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putLong("SessionStartTimeInMillis", SessionStartTimeInMillis);
editor.putLong("DROStartTimeInMillis", DROStartTimeInMillis);
editor.putLong("MillisLeft", SessionTimeLeftInMillis);
editor.putLong("DROMillisLeft", DROTimeLeftInMillis);
editor.putBoolean("TimerRunning", SessionTimerRunning);
editor.putBoolean("DROTimerRunning", DROTimerRunning);
editor.putLong("EndTime", SessionEndTime);
editor.putLong("DROEndTime", DROEndTime);
if (sessionTimer !=null); {
sessionTimer.cancel();
}
}
#Override
public void onStart() {
super.onStart();
SharedPreferences preferences = this.getActivity().getSharedPreferences("prefs", Context.MODE_PRIVATE);
SessionStartTimeInMillis = preferences.getLong("SessionStartTimeInMillis", 0);
SessionTimeLeftInMillis = preferences.getLong("MillisLeft", SessionTimeLeftInMillis);
SessionTimerRunning = preferences.getBoolean("TimerRunning", false);
DROStartTimeInMillis = preferences.getLong("DROStartTimeInMillis", 0);
DROTimeLeftInMillis = preferences.getLong("DROMillisLeft", DROTimeLeftInMillis);
DROTimerRunning = preferences.getBoolean("DROTimerRunning", false);
updateSessionText();
updateSessionInterface();
updateDROText();
updateDROInterface();
if (SessionTimerRunning) {
SessionEndTime = preferences.getLong("EndTime", 0);
SessionTimeLeftInMillis = SessionEndTime - System.currentTimeMillis();
if (SessionTimeLeftInMillis <0) {
SessionTimeLeftInMillis = 0;
SessionTimerRunning = false;
} else {
startSessionTimer();
}
}
if (DROTimerRunning) {
DROEndTime = preferences.getLong("EndTime", 0);
DROTimeLeftInMillis = DROEndTime - System.currentTimeMillis();
if (DROTimeLeftInMillis <0) {
DROTimeLeftInMillis = 0;
DROTimerRunning = false;
} else {
startDROTimer();
}
}
}
}
Related
I designed an online music player which streams file from my server and shows them in a recyclerView. when I click on the specific song it plays automatically, but when I go back to my recyclerview and I choose it again it doesn't play anymore and even duration of song becomes an insane number. I even tried to handle my media player onBackPressed() method or in OnRestart() or onResume() method. I'm confused. Please let me know about any suggestion.
Here's my code for player:
public class PlayerActivity extends AppCompatActivity implements View.OnClickListener {
private CircularMusicProgressBar cover;
private ImageButton playPause, rewind, forward, repeat, fav, download;
private TextView title, term, spendingTime, totalTime, emptyRec;
private Context context;
public static MediaPlayer mediaPlayer;
int i;
private Timer timer;
private Bundle extra;
Uri uri;
RecyclerView recyclerView;
RequestQueue requestQueue;
SuggestionAdapter suggestionAdapter;
LinearLayoutManager layoutManager;
List<Listening> list = new ArrayList<>();
String url = "https://www.learnhere.ir/listening.php";
AccessDataOnServer suggestionData;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_player);
initFields();
playOnStart();
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.play_pause:
if (i == 1) {
i = 2;
playPause.setImageResource(R.drawable.ic_baseline_play);
mediaPlayer.pause();
} else {
i = 1;
playPause.setImageResource(R.drawable.ic_baseline_pause);
int b = mediaPlayer.getCurrentPosition();
mediaPlayer.seekTo(b);
mediaPlayer.start();
}
break;
case R.id.rewind:
Toast.makeText(context, "rewind", Toast.LENGTH_SHORT).show();
break;
case R.id.forward:
Toast.makeText(context, "forward", Toast.LENGTH_SHORT).show();
break;
case R.id.repeat:
Toast.makeText(context, "repeat", Toast.LENGTH_SHORT).show();
break;
case R.id.fav:
Toast.makeText(context, "fav", Toast.LENGTH_SHORT).show();
break;
case R.id.download:
Toast.makeText(context, "download", Toast.LENGTH_SHORT).show();
break;
}
}
public void initFields() {
i = 0;
context = this;
cover = findViewById(R.id.cover_progress);
playPause = findViewById(R.id.play_pause);
rewind = findViewById(R.id.rewind);
forward = findViewById(R.id.forward);
repeat = findViewById(R.id.repeat);
fav = findViewById(R.id.fav);
download = findViewById(R.id.download);
title = findViewById(R.id.title_tv);
term = findViewById(R.id.term_tv);
emptyRec = findViewById(R.id.empty_rec);
spendingTime = findViewById(R.id.spending_time);
totalTime = findViewById(R.id.total_time);
playPause.setOnClickListener(this);
rewind.setOnClickListener(this);
forward.setOnClickListener(this);
repeat.setOnClickListener(this);
fav.setOnClickListener(this);
download.setOnClickListener(this);
extra = getIntent().getExtras();
Picasso.get().load(extra.getString("cover")).into(cover);
title.setText(extra.getString("title"));
term.setText(extra.getString("term"));
timer = new Timer();
uri = Uri.parse(extra.getString("link"));
recyclerView = findViewById(R.id.suggestion_recycler);
requestQueue = Volley.newRequestQueue(context);
layoutManager = new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false);
suggestionAdapter = new SuggestionAdapter(list, context);
suggestionData = new AccessDataOnServer();
recyclerView.setAdapter(suggestionAdapter);
recyclerView.setLayoutManager(layoutManager);
suggestionData.getSuggestion(context, list, recyclerView, url, requestQueue);
if (list.size() <= 1) {
emptyRec.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.GONE);
} else {
emptyRec.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
}
}
public void play() {
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
try {
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioAttributes(
new AudioAttributes
.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build());
mediaPlayer.setDataSource(context, uri);
mediaPlayer.prepare();
mediaPlayer.start();
} catch (Exception e) {
e.printStackTrace();
}
runOnUiThread(new Runnable() {
#Override
public void run() {
try {
getTime();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
});
thread.start();
}
public void playOnStart() {
Toast.makeText(context, "Please wait until the audio plays...", Toast.LENGTH_SHORT).show();
play();
i = 1;
playPause.setImageResource(R.drawable.ic_baseline_pause);
}
public String millisecondToSecond(long millisecond) {
String finalTimerString = "";
String secondString = "";
String minuteString = "";
//convert total duration into time
int hour = (int) (millisecond / (1000 * 60 * 60));
int minute = (int) (millisecond % (1000 * 60 * 60) / (1000 * 60));
int second = (int) (millisecond % (1000 * 60 * 60) % (1000 * 60) / 1000);
//Add hours if there
if (hour > 0) {
finalTimerString = hour + ":";
}
//Prepending 0 to second if it's one digit
if (second < 10) {
secondString = "0" + second;
} else {
secondString = "" + second;
}
//Prepending 0 to minute if it's one digit
if (minute < 10) {
minuteString = "0" + minute;
} else {
minuteString = "" + minute;
}
finalTimerString = finalTimerString + minuteString + ":" + secondString;
//Return timer string
return finalTimerString;
}
public void getTime() {
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
// Without runOnUiThread we don't have access to modifiers like text view
runOnUiThread(new Runnable() {
#Override
public void run() {
long current = mediaPlayer.getCurrentPosition();
int i = (mediaPlayer.getCurrentPosition() * 100) / mediaPlayer.getDuration();
spendingTime.setText("" + millisecondToSecond(current));
cover.setValue(i);
int duration = mediaPlayer.getDuration();
String time = String.format(Locale.US, "%02d:%02d",
TimeUnit.MILLISECONDS.toMinutes(duration),
TimeUnit.MILLISECONDS.toSeconds(duration) -
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(duration)));
totalTime.setText(time);
}
});
}
}, 0, 1000);
}
#Override
public void onBackPressed() {
mediaPlayer.stop();
finish();
}
#Override
protected void onRestart() {
super.onRestart();
playOnStart();
}
Well I reviewed your codes and it seems you aren't using Media player class properly.
I made a few changes to each block of your codes. I hope it solves your problem.
First of all change the following scope:
public static MediaPlayer mediaPlayer;
To
MediaPlayer mediaPlayer = new MediaPlayer();
I made a method for your mediaPlayer and you should use it in your onCreate() :
private void prepareMediaPlayer() {
try {
mediaPlayer.setDataSource(context, uri);
mediaPlayer.prepare();
updateSeekBarTimer();
} catch (Exception e) {
e.printStackTrace();
}
}
A method to update your seekbar :
private void updateSeekBarTimer() {
try {
if (mediaPlayer.isPlaying()) {
total_time = mediaPlayer.getDuration();
current_time = mediaPlayer.getCurrentPosition();
totalTime.setText("" + utils.milliSecondsToTimer(total_time));
spendingTime.setText("" + utils.milliSecondsToTimer(current_time));
int progress = (int) (utils.getProgressPercentage(current_time, total_time));
seekBar.setProgress(progress);
Runnable runnable = new Runnable() {
#Override
public void run() {
updateSeekBarTimer();
}
};
handler.postDelayed(runnable, 1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
And finally you need an Interface to decide what happens when your track ends:
public class PlayerActivity extends AppCompatActivity implements View.OnClickListener, MediaPlayer.OnCompletionListener
#Override
public void onCompletion(MediaPlayer mp) {
handler.removeCallbacks(null);
playPause.setImageResource(R.drawable.ic_baseline_play);
if (rep) {
mediaPlayer.start();
playPause.setImageResource(R.drawable.ic_baseline_pause);
updateSeekBarTimer();
} else {
seekBar.setProgress(0);
totalTime.setText(R.string.zero_time);
spendingTime.setText(R.string.zero_time);
playPause.setImageResource(R.drawable.ic_baseline_play);
mediaPlayer.reset();
prepareMediaPlayer();
}
}
I have a timer that I want to loop as soon as it reaches 0.
Resetting the timer in onFinish() seems to be slower than manually resetting the timer via the timer's reset button.
When the timer finishes, it doesn't restart until "0" has shown for a second (maybe at the end of the tick,) effectively adding an extra second to the timer that I don't want.
How can I make this timer restart the instant "0" is displayed?
Imports and fragment
package com.example.datacollector;
import android.content.Context;
import android.content.SharedPreferences;
import android.media.AudioManager;
import android.media.ToneGenerator;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.TextView;
import android.widget.EditText;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import java.util.Locale;
public class fragmentdro extends Fragment {
private EditText session_edit_text;
private EditText dro_edit_text;
private TextView session_text_view;
private TextView dro_text_view;
private Button session_start_button;
private Button dro_start_button;
private Button session_reset_button;
private Button dro_reset_button;
private Button session_set_button;
private Button dro_set_button;
private CountDownTimer sessionTimer;
private CountDownTimer droTimer;
private boolean SessionTimerRunning;
private boolean DROTimerRunning;
private long SessionStartTimeInMillis;
private long DROStartTimeInMillis;
private long SessionTimeLeftInMillis = SessionStartTimeInMillis;
private long DROTimeLeftInMillis = DROStartTimeInMillis;
private long SessionEndTime;
private long DROEndTime;
View View;
public View onCreateView(#NonNull LayoutInflater inflater, #NonNull ViewGroup container, #NonNull Bundle savedInstanceState) {
View = inflater.inflate(R.layout.dro_fragment, container, false);
session_edit_text = View.findViewById(R.id.session_edit_text);
dro_edit_text = View.findViewById(R.id.dro_edit_text);
session_text_view = View.findViewById(R.id.session_text_view);
dro_text_view = View.findViewById(R.id.dro_text_view);
session_start_button = View.findViewById(R.id.session_start_button);
dro_start_button = View.findViewById(R.id.dro_start_button);
session_reset_button = View.findViewById(R.id.session_reset_button);
dro_reset_button = View.findViewById(R.id.dro_reset_button);
session_set_button = View.findViewById(R.id.session_set_button);
dro_set_button = View.findViewById(R.id.dro_set_button);
InputMethodManager imm = (InputMethodManager)getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
ToneGenerator toneGenerator = new ToneGenerator(AudioManager.STREAM_ALARM, 100);
Buttons
dro_set_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(android.view.View view) {
String DROinput = dro_edit_text.getText().toString();
if (DROinput.length() == 0) {
Toast.makeText(getActivity(), "Fill it in", Toast.LENGTH_SHORT).show();
return;
}
long millisInput = Long.parseLong(DROinput) * 1000;
if (millisInput == 0) {
Toast.makeText(getActivity(), "Please enter a positive number", Toast.LENGTH_SHORT).show();
return;
}
setDROTime(millisInput);
dro_edit_text.setText("");
imm.hideSoftInputFromWindow(getView().getWindowToken(), 0);
}
});
dro_start_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(android.view.View view) {
if (DROTimerRunning) {
pauseDROTimer();
} else {
startDROTimer();
}
}
});
dro_reset_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(android.view.View view) {
if (DROTimerRunning) {
resetDROTimer();
} else {
resetDROTimer();
}
}
});
return View;
}
Timer
public void setDROTime(long milliseconds) {
DROStartTimeInMillis = milliseconds;
resetDROTimer();
}
public void startDROTimer() {
DROEndTime = System.currentTimeMillis() + DROTimeLeftInMillis;
final int[] secondsLeft = {0};
droTimer = new CountDownTimer(DROTimeLeftInMillis, 100) {
#Override
public void onTick(long ms) {
if (Math.round((float)ms/1000.0f) != secondsLeft[0])
{
secondsLeft[0] = (int) (Math.round((float)ms)/1000.0f);
}
DROTimeLeftInMillis = ms;
updateDROText();
}
#Override
public void onFinish() {
dro_text_view.setText("0");
DROTimerRunning = false;
DROTimeLeftInMillis = DROStartTimeInMillis + 1000;
updateDROText();
updateDROInterface();
startDROTimer();
}
}.start();
DROTimerRunning = true;
dro_start_button.setText("Pause");
}
public void pauseDROTimer() {
droTimer.cancel();
DROTimerRunning = false;
dro_start_button.setText("Start");
updateDROText();
}
public void resetDROTimer() {
if (DROTimerRunning) {
droTimer.cancel();
DROTimeLeftInMillis = (DROStartTimeInMillis + 1000);
updateDROInterface();
startDROTimer();
} else {
DROTimeLeftInMillis = (DROStartTimeInMillis);
updateDROText();
updateDROInterface();
dro_reset_button.setVisibility(android.view.View.VISIBLE);
dro_start_button.setVisibility(android.view.View.VISIBLE);
}
}
public void updateDROText() {
int seconds = (int) (DROTimeLeftInMillis/1000)%60;
String timeLeftFormatted = String.format(Locale.getDefault(),
timeLeftFormatted = String.format(Locale.getDefault(), ":%02d", seconds));
dro_text_view.setText(timeLeftFormatted);
}
public void updateDROInterface() {
if (DROTimerRunning) {
dro_edit_text.setVisibility(android.view.View.INVISIBLE);
dro_set_button.setVisibility(android.view.View.INVISIBLE);
dro_reset_button.setVisibility(android.view.View.VISIBLE);
dro_start_button.setText("Pause");
} else {
dro_edit_text.setVisibility(android.view.View.VISIBLE);
dro_set_button.setVisibility(android.view.View.VISIBLE);
dro_reset_button.setVisibility(android.view.View.VISIBLE);
dro_start_button.setText("Start");
}
}
Prefs
#Override
public void onStop() {
super.onStop();
SharedPreferences preferences = this.getActivity().getSharedPreferences("prefs", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putLong("SessionStartTimeInMillis", SessionStartTimeInMillis);
editor.putLong("DROStartTimeInMillis", DROStartTimeInMillis);
editor.putLong("MillisLeft", SessionTimeLeftInMillis);
editor.putLong("DROMillisLeft", DROTimeLeftInMillis);
editor.putBoolean("TimerRunning", SessionTimerRunning);
editor.putBoolean("DROTimerRunning", DROTimerRunning);
editor.putLong("EndTime", SessionEndTime);
editor.putLong("DROEndTime", DROEndTime);
if (sessionTimer !=null); {
sessionTimer.cancel();
}
}
#Override
public void onStart() {
super.onStart();
SharedPreferences preferences = this.getActivity().getSharedPreferences("prefs", Context.MODE_PRIVATE);
SessionStartTimeInMillis = preferences.getLong("SessionStartTimeInMillis", 0);
SessionTimeLeftInMillis = preferences.getLong("MillisLeft", SessionTimeLeftInMillis);
SessionTimerRunning = preferences.getBoolean("TimerRunning", false);
DROStartTimeInMillis = preferences.getLong("DROStartTimeInMillis", 0);
DROTimeLeftInMillis = preferences.getLong("DROMillisLeft", DROTimeLeftInMillis);
DROTimerRunning = preferences.getBoolean("DROTimerRunning", false);
updateSessionText();
updateSessionInterface();
updateDROText();
updateDROInterface();
if (SessionTimerRunning) {
SessionEndTime = preferences.getLong("EndTime", 0);
SessionTimeLeftInMillis = SessionEndTime - System.currentTimeMillis();
if (SessionTimeLeftInMillis <0) {
SessionTimeLeftInMillis = 0;
SessionTimerRunning = false;
} else {
startSessionTimer();
}
}
if (DROTimerRunning) {
DROEndTime = preferences.getLong("EndTime", 0);
DROTimeLeftInMillis = DROEndTime - System.currentTimeMillis();
if (DROTimeLeftInMillis <0) {
DROTimeLeftInMillis = 0;
ToneGenerator toneGenerator = new ToneGenerator(AudioManager.STREAM_ALARM, 200);
toneGenerator.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 100);
DROTimerRunning = false;
} else {
startDROTimer();
}
}
}
}
You can add an extra 100 ms to your DROTimeLeftInMillis
so it should look like: new CountDownTimer(DROTimeLeftInMillis + 100, 100) { ..
because the onTick method doesn't call immediately, it called a few milliseconds after, so by adding the 100 ms it should deal with the delay and give you the result you expect.
I'm working on a fragment that includes a timer and a counter. The initial timer value is an edit text. When the timer reaches 0, I want to calculate a rate (# displayed on counter/edit text input.) I've tried to convert both values to doubles, but it seems I went wrong somewhere. When the timer reaches 0, the rate displays "infinity." Any help would be greatly appreciated!
IDs
package com.example.datacollector;
import android.content.ClipData;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.TextView;
import android.widget.EditText;
import android.widget.Toast;
import androidx.fragment.app.Fragment;
import androidx.annotation.NonNull;
import java.util.Locale;
public class fragmentrate extends Fragment {
private EditText edit_text_input;
private TextView text_view_countdown;
private TextView text_view_frequency;
private TextView text_view_rate;
private TextView rate_equals;
private Button button_start_pause;
private Button button_reset;
private Button button_set;
private Button button_frequency;
private Button button_reset_frequency;
private CountDownTimer countDownTimer;
private boolean mTimerRunning;
private long mStartTimeInMillis;
private long mTimeLeftInMillis = mStartTimeInMillis;
private long mEndTime;
private int mCounter;
private double denominator;
View View;
public View onCreateView(#NonNull LayoutInflater inflater, #NonNull ViewGroup container, #NonNull Bundle savedInstanceState) {
View = inflater.inflate(R.layout.rate_fragment, container, false);
text_view_countdown = View.findViewById(R.id.text_view_countdown);
button_start_pause = View.findViewById(R.id.button_start_pause);
button_reset = View.findViewById(R.id.button_reset);
button_frequency = View.findViewById(R.id.button_frequency);
button_reset_frequency = View.findViewById(R.id.button_reset_frequency);
edit_text_input = View.findViewById(R.id.edit_text_input);
button_set = View.findViewById(R.id.button_set);
text_view_frequency = View.findViewById(R.id.text_view_frequency);
text_view_rate = View.findViewById(R.id.text_view_rate);
rate_equals = View.findViewById(R.id.rate_equals);
Counter
button_frequency.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(android.view.View view) {
mCounter ++;
text_view_frequency.setText(Integer.toString(mCounter));
}
});
button_reset_frequency.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(android.view.View view) {
mCounter = 0;
text_view_frequency.setText(Integer.toString(mCounter));
}
});
Timer and rate calculation
button_set.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(android.view.View view) {
String input = edit_text_input.getText().toString();
if (input.length() == 0) {
Toast.makeText(getActivity(), "Fill it in, loser", Toast.LENGTH_SHORT).show();
return;
}
long millisInput = Long.parseLong(input) * 60000;
if (millisInput == 0) {
Toast.makeText(getActivity(), "Please enter a positive number", Toast.LENGTH_SHORT).show();
return;
}
setTime(millisInput);
edit_text_input.setText("");
}
});
button_start_pause.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(android.view.View view) {
if (mTimerRunning) {
pauseTimer();
} else {
startTimer();
}
}
});
button_reset.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(android.view.View view) {
if(mTimerRunning) {
resetTimer();
} else{
resetTimer();
}
}
});
return View;
}
private void setTime(long milliseconds) {
mStartTimeInMillis = milliseconds;
resetTimer();
}
private void startTimer() {
mEndTime = System.currentTimeMillis() + mTimeLeftInMillis;
countDownTimer = new CountDownTimer(mTimeLeftInMillis, 100) {
#Override
public void onTick(long millisUntilFinished) {
mTimeLeftInMillis = millisUntilFinished;
updateCountDownText();
}
#Override
public void onFinish() {
mTimerRunning = false;
button_start_pause.setText("Start");
button_start_pause.setVisibility(android.view.View.INVISIBLE);
button_reset.setVisibility(android.view.View.VISIBLE);
try {
double denominator = Integer.parseInt(edit_text_input.getText().toString());
}
catch (NumberFormatException e)
{
double denominator = 0;
}
double rate = ((double)mCounter/denominator);
text_view_rate.setText(Double.toString(rate));
}
}.start();
mTimerRunning = true;
button_start_pause.setText("Pause");
}
private void pauseTimer() {
countDownTimer.cancel();
mTimerRunning = false;
updateCountDownText();
button_start_pause.setText("Start");
}
private void resetTimer() {
if (mTimerRunning) {
countDownTimer.cancel();
mTimeLeftInMillis = (mStartTimeInMillis + 1000);
updateWatchInterface();
startTimer();
} else {
}
mTimeLeftInMillis = (mStartTimeInMillis);
updateCountDownText();
updateWatchInterface();
button_reset.setVisibility(android.view.View.VISIBLE);
button_start_pause.setVisibility(android.view.View.VISIBLE);
}
private void updateCountDownText() {
int minutes = (int) (mTimeLeftInMillis/1000)/60;
int seconds = (int) (mTimeLeftInMillis/1000)%60;
String timeLeftFormatted = String.format(Locale.getDefault(),
timeLeftFormatted = String.format(Locale.getDefault(), "%02d:%02d", minutes, seconds));
text_view_countdown.setText(timeLeftFormatted);
}
private void updateWatchInterface() {
if (mTimerRunning) {
edit_text_input.setVisibility(android.view.View.INVISIBLE);
button_set.setVisibility(android.view.View.INVISIBLE);
button_reset.setVisibility(android.view.View.VISIBLE);
button_start_pause.setText("Pause");
} else {
edit_text_input.setVisibility(android.view.View.VISIBLE);
button_set.setVisibility(android.view.View.VISIBLE);
button_reset.setVisibility(android.view.View.VISIBLE);
button_start_pause.setText("Start");
if (mTimeLeftInMillis <100) {
button_start_pause.setVisibility(android.view.View.INVISIBLE);
} else {
button_start_pause.setVisibility(android.view.View.VISIBLE);
}
}
}
#Override
public void onStop() {
super.onStop();
SharedPreferences preferences = this.getActivity().getSharedPreferences("prefs", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putLong("startTimeInMillis", mStartTimeInMillis);
editor.putLong("millisLeft", mTimeLeftInMillis);
editor.putBoolean("timerRunning", mTimerRunning);
editor.putLong("endTime", mEndTime);
editor.apply();
if(countDownTimer !=null); {
countDownTimer.cancel();
}
}
#Override
public void onStart() {
super.onStart();
SharedPreferences preferences = this.getActivity().getSharedPreferences("prefs", Context.MODE_PRIVATE);
mStartTimeInMillis = preferences.getLong("startTimeInMillis", 0);
mTimeLeftInMillis = preferences.getLong("millisLeft", mStartTimeInMillis);
mTimerRunning = preferences.getBoolean("timerRunning", false);
updateCountDownText();
updateWatchInterface();
if (mTimerRunning) {
mEndTime = preferences.getLong("endTime", 0);
mTimeLeftInMillis = mEndTime - System.currentTimeMillis();
if (mTimeLeftInMillis <0) {
mTimeLeftInMillis = 0;
mTimerRunning = false;
updateCountDownText();
updateWatchInterface();
} else {
startTimer();
}
}
}
You are specifically capturing a NumberFormatException, setting the denominator to zero, and then you're dividing by that number. By design, your result will be Infinity.
Quoting from the Java 8 JLS:
Division of a nonzero finite value by a zero results in a signed infinity. The sign is determined by the rule stated above.
https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.17.2
I'm creating a timer app that utilizes a thread. When I exit the app using the home button the timer is running fine. Usually, when the time is up a dialog is launched that asks the user for some input. This works completely fine if the app is in its onResume() state, however when the app is in its onStop() state the dialog will not launch and an error is thrown.
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
How can I make it so that when the time is up and the app is NOT in the foreground the dialog is still launched. My initial thought was to save the time remaining in the bundle, but the time remaining is changing for every single tick. Then I thought about storing a boolean in the bundle mTimeRunning. However, when the time is up this value must also change. So now I'm drawing a blank. What can I possibly do so that the dialog is launched when the app is not in the foreground?
TimerActivity.java
public class TimerActivity extends AppCompatActivity implements TimeDialogFragment.sendMinutes,
TimeFinishDialogFragment.sendResponse, BreakFinishDialogFragment.userResponse {
// Variable to log activity state
private static final String TAG = "TimerActivity";
private static final boolean DEBUG = true;
// ^^ Variable used to log acitivty state
private Handler mHandler;
private Runnable mRunnable;
//private static final long START_TIME_MILLISECONDS = 600000;
// Below start time is for development purposes only
private static long mStartTime = 10000;
private long mTimeRemaining = mStartTime;
private boolean mTimeRunning;
private boolean mBreakTime = false;
private ProgressBar mTimeBar;
private TextView mTime;
private Button mStartPause;
private Button mSetTime;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_timer);
if(DEBUG) Log.d(TAG, "+ onCreate() +");
mHandler = new Handler();
mTimeBar = findViewById(R.id.time_bar);
mTime = findViewById(R.id.text_view_time);
mStartPause = findViewById(R.id.button_start_pause);
mSetTime = findViewById(R.id.button_set_time);
updateCountDownText();
mTimeBar.setMax((int) mTimeRemaining);
mSetTime.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//FragmentManager manager = getSupportFragmentManager();
DialogFragment setTime = new TimeDialogFragment();
setTime.show(getFragmentManager(),"SET_TIME_DIALOG");
}
});
mStartPause.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(mTimeRunning){
//pauseTimer();
mTimeRunning = false;
mStartPause.setText("Start");
mHandler.removeCallbacks(mRunnable);
}else{
//startTimer();
timer();
}
}
});
}
// Using a handler + anon runnable
private void timer(){
mRunnable = new Runnable() {
#Override
public void run() {
mTimeRunning = true;
mStartPause.setText("Pause");
mTimeRemaining = mTimeRemaining - 1000;
updateCountDownText();
mTimeBar.incrementProgressBy(1000);
if(mTimeRemaining > 0) {
mHandler.postDelayed(this, 1000);
}else{
// if breaktime is false
if(!mBreakTime) {
DialogFragment dialog = new TimeFinishDialogFragment();
dialog.show(getFragmentManager(),"TIME_FINISH_DIALOG");
mTimeRunning = false;
}else{
// launch break time up dialog.
mBreakTime = false;
DialogFragment dialog = new BreakFinishDialogFragment();
dialog.show(getFragmentManager(), "BREAK_FINSIH_DIALOG");
}
}
}
};
mHandler.postDelayed(mRunnable,1000);
}
public void updateCountDownText(){
int min = (int) (mTimeRemaining / 1000) / 60;
int sec = (int) (mTimeRemaining / 1000) % 60;
String formattedString = String.format(Locale.getDefault(), "%02d:%02d", min, sec);
mTime.setText(formattedString);
}
public void setCountDownText(long time){
int min = (int) (time / 1000) / 60;
int sec = (int) (time / 1000) % 60;
String formattedString = String.format(Locale.getDefault(), "%02d:%02d", min, sec);
mTime.setText(formattedString);
}
#Override
public void userTime(int minutes) {
TimerActivity.mStartTime = (minutes * 60) * 1000;
mTimeRemaining = TimerActivity.mStartTime;
mTimeBar.setMax((int) mTimeRemaining);
setCountDownText(mTimeRemaining);
}
#Override
public void sendResponse(int val) {
if(val == -1){
mTimeRemaining = TimerActivity.mStartTime;
mTimeBar.setMax((int) mTimeRemaining);
updateCountDownText();
mTimeBar.setProgress(0);
mStartPause.setText("Start");
}else if(val == 1) {
mBreakTime = true;
mTimeRemaining = 15000;
mTimeBar.setMax((int) mTimeRemaining);
setCountDownText(mTimeRemaining);
mTimeBar.setProgress(0);
mStartPause.setVisibility(View.INVISIBLE);
timer();
}else {
mTimeRemaining = TimerActivity.mStartTime;
mTimeBar.setMax((int) mTimeRemaining);
updateCountDownText();
mTimeBar.setProgress(0);
timer();
}
}
#Override
public void userResponse(int val) {
if(val < 0) {
// user clicked cance
mTimeRemaining = TimerActivity.mStartTime;
mTimeBar.setMax((int) mTimeRemaining);
updateCountDownText();
mTimeBar.setProgress(0);
mStartPause.setText("Start");
}else {
mTimeRemaining = TimerActivity.mStartTime;
mTimeBar.setMax((int) mTimeRemaining);
updateCountDownText();
mTimeBar.setProgress(0);
timer();
}
mStartPause.setVisibility(View.VISIBLE);
}
TimeFinishedDialog.java
public class TimeFinishDialogFragment extends DialogFragment implements View.OnClickListener{
private Button mCancel;
private Button mSkip;
private Button mStartBreak;
private sendResponse mResponse;
interface sendResponse{
void sendResponse(int val);
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
//return super.onCreateDialog(savedInstanceState);
View view = LayoutInflater.from(getActivity()).inflate(R.layout.time_finish_dialog_fragment, null, false);
mCancel = view.findViewById(R.id.button_cancel);
mSkip = view.findViewById(R.id.button_skip);
mStartBreak = view.findViewById(R.id.button_start_break);
mCancel.setOnClickListener(this);
mSkip.setOnClickListener(this);
mStartBreak.setOnClickListener(this);
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setView(view)
.setTitle("Start Break?");
AlertDialog dialog = builder.create();
dialog.setContentView(view);
return dialog;
}
#Override
public void onClick(View view) {
switch(view.getId()){
case R.id.button_cancel:
mResponse.sendResponse(-1);
getDialog().dismiss();
break;
case R.id.button_skip:
mResponse.sendResponse(0);
getDialog().dismiss();
break;
case R.id.button_start_break:
mResponse.sendResponse(1);
getDialog().dismiss();
break;
default:
break;
}
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
try{
mResponse = (sendResponse) getActivity();
}catch (ClassCastException e){
System.out.println("Error: " + e);
}
}
}
when the time is up and the app is NOT in the foreground the dialog is still launched
You should not be doing this since this will interrupt the user from doing other work which they might be doing at that time and this can be irritating.
What can I possibly do so that the dialog is launched when the app is not in the foreground?
You can show the dialog only the user is actively using your application or you can fall back to show a notification when the user is not using the app.
And if you desperately want to show a dialog, you can try a dialog themed activity
public class MainActivity extends AppCompatActivity {
int foulCounterA = 0;
int scoreOnePointTeamA = 0;
int periodCount = 0;
private TextView tv1;
private TextView period;
private Button startbtn, cancelbtn;
private ToggleButton togbtn;
private boolean isPaused = false;
private boolean isCanceled = false;
private long remainingTime = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv1 = (TextView) findViewById(R.id.tv1);
period = (TextView) findViewById(R.id.period_number);
startbtn = (Button) findViewById(R.id.startBtn);
cancelbtn = (Button) findViewById(R.id.cancelBtn);
togbtn = (ToggleButton) findViewById(R.id.togBtn);
cancelbtn.setEnabled(false);
togbtn.setEnabled(false);
startbtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
foulCounterA = 0;
foulCounterB = 0;
displayForTeamAFoul(foulCounterA);
displayForTeamBFoul(foulCounterB);
if (periodCount < 3)
periodCount = periodCount + 1;
else periodCount = 4;
period.setText("Period " + periodCount);
startbtn.setEnabled(false);
cancelbtn.setEnabled(true);
togbtn.setEnabled(true);
isPaused = false;
isCanceled = false;
long millisInFuture = 20000; /////20sec
long countDownInterval = 1000; /////1sec
new CountDownTimer(millisInFuture, countDownInterval) {
#Override
public void onTick(long millisUntilFinished) {
if (isPaused || isCanceled) {
cancel();
} else {
tv1.setText("" + millisUntilFinished / 1000);
remainingTime = millisUntilFinished;
}
}
#Override
public void onFinish() {
startbtn.setEnabled(true);
togbtn.setEnabled(false);
if (periodCount < 4)
tv1.setText("Times up!");
else tv1.setText("Game Over!");
}
}.start();
}
});
togbtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (togbtn.isChecked()) {
isPaused = true;
} else {
isPaused = false;
long millisInFuture = remainingTime;
long countDownInterval = 1000; /////1sec
new CountDownTimer(millisInFuture, countDownInterval) {
#Override
public void onTick(long millisUntilFinished) {
if (isPaused || isCanceled) {
cancel();
} else {
tv1.setText("" + millisUntilFinished / 1000);
remainingTime = millisUntilFinished;
}
}
#Override
public void onFinish() {
startbtn.setEnabled(true);
togbtn.setEnabled(false);
if (periodCount < 4)
tv1.setText("Times up!");
else tv1.setText("Game Over!");
}
}.start();
}
}
});
cancelbtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
isCanceled = true;
period.setText("Period");
tv1.setText("Timer");
startbtn.setEnabled(true);
togbtn.setEnabled(false);
cancelbtn.setEnabled(false);
foulCounterA = 0;
foulCounterB = 0;
periodCount = 0;
displayForTeamAFoul(foulCounterA);
displayForTeamBFoul(foulCounterB);
}
});
}
public void onePointForTeamA(View v) {
scoreTeamA = scoreTeamA + 1;
scoreOnePointTeamA = scoreOnePointTeamA + 1;
displayForTeamA(scoreTeamA);
displayForTeamAOnePoint(scoreOnePointTeamA);
}
public void foulCountForTeamA(View v) {
if (foulCounterA < 5)
foulCounterA = foulCounterA + 1;
else
foulCounterA = 5;
displayForTeamAFoul(foulCounterA);
}
public void displayForTeamAOnePoint(int score) {
TextView scoreView = (TextView) findViewById(R.id.team_a_score_1_point);
scoreView.setText(String.valueOf(score));
}
public void displayForTeamAFoul(int score) {
TextView scoreView = (TextView) findViewById(R.id.team_a_foul);
scoreView.setText(String.valueOf(score));
}
}
I wanted to make the java code as simple as I can so I've just added the lines for my question. What I'm trying to do is
android:onClick="onePointForTeamA"make this button only clickable when foulCounterA = 5 Failed to add if (foulCounterA = 5); inside public void foulCountForTeamA(View v) {
It gave me an error that way. Says required: boolean, found: int.
What should I do with the code? Any help will be appreeciated
Regarding your concrete question, the syntax of if (foulCounterA = 5); is wrong, because the equation check is have to made by == operator.
So the correct syntax would be if (foulCounterA == 5);
As #OH GOD SPIDERS wrote in the comment, you should check the basics of java operators.
Also I recommend You to search for the answer before asking a new question.