Android Quiz Game - Countdown timer for each qstion

android, countdown, java, timer

Solution

I think the activity doesn't exist anymore at a certain point when you try to make the dialog(probably when the `CountDownTimer` is near the end?!?).

Anyway I think finishing and starting the same activity for each question isn't such a good idea, instead you could use the current activity and simply restart the timer. For example:

public class QuestionActivity extends SherlockActivity implements
    OnClickListener {

   private CountDownTimer mCountDown;

   @Override
   public void onCreate(Bundle savedInstanceState) {
      // ...
      mCountDown = new CountDownTimer(20000, 1000) {

        @Override
        public void onFinish() {
            // myCounter.setText("Time up!");
            timeUp(context);
        }

        @Override
        public void onTick(long millisUntilFinished) {
            myCounter.setText("Time left: "
                    + String.valueOf(millisUntilFinished / 1000));
        }
      }.start();
      // ... 

and in the `onClick` callback do the same to setup a new question, stop the old timer and restart the new timer:

//check if end of game
if (currentGame.isGameOver()) {
    Intent i = new Intent(this, EndgameActivity.class);
    startActivity(i);
    finish();
} else {
    if (mCountDown != null) { 
       mCountDown.cancel();
    }  
    currentQ = currentGame.getNextQuestion();
    setQuestions();
    mCountDown = new CountDownTimer(20000, 1000) {

        @Override
        public void onFinish() {
            // myCounter.setText("Time up!");
            timeUp(context);
        }

        @Override
        public void onTick(long millisUntilFinished) {
            myCounter.setText("Time left: "
                    + String.valueOf(millisUntilFinished / 1000));
        }
    }.start();  
}

Also, in the callback for the `Dialog`'s `Button` I would first close the `Dialog` before finishing the `Activity`:

((AlertDialog) dialog).dismiss();
QuestionActivity.this.finish();

Problem

I have created a Quiz app for Android using the tutorial here: http://automateddeveloper.blogspot.co.uk/2011/06/getting-started-complete-android-app.html For each question, the user will only have 20 seconds to answer it. If he/she fails to answer in 20 seconds, an `AlertDialog` will popup and the game will terminate. To do this, I have added a counter in the `OnCreate` method of `QuestionActivity class`: ``` final TextView myCounter = (TextView) findViewById(R.id.countdown); new CountDownTimer(20000, 1000) { @Override public void onFinish() { timeUp(); } @Override public void onTick(long millisUntilFinished) { myCounter.setText("Time left: " + String.valueOf(millisUntilFinished / 1000)); } }.start(); public void timeUp() { AlertDialog.Builder builder = new AlertDialog.Builder( QuestionActivity.this); builder.setTitle("Times up!") .setMessage("Game over") .setCancelable(false) .setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { QuestionActivity.this.finish(); } }); AlertDialog alert = builder.create(); alert.show(); } ``` The counter works displays and functions on screen correctly. After answering a question, the activity moves to the next question and the counter resets itself to 20 seconds again. The problem The quiz has 15 questions, after answering 3 or 4 questions, the app crashes and I get the following error: ``` 07-18 00:49:05.530: E/AndroidRuntime(4867): android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@41533a80 is not valid; is your activity running? ``` I believe this relates to the `AlertDialog`. I have looked up this error code on Stackoverflow and the popular solution is to pass `ActivityName.this` as the context when building the `AlertDialog`. Unfortunately this does not solve the problem. I believe the counter is setting a time limit of 20 seconds for the whole activity. My requirement is 20 seconds for each question. However, the counter resets to 20 seconds when the user press the' `Next` button and the activity moves to the next question. Should I be resetting the counter when the user presses the `Next` button? Here is the `OnClickListener` code for the `Next` button ``` if (currentGame.isGameOver()) { Intent i = new Intent(this, EndgameActivity.class); startActivity(i); finish(); } else { Intent i = new Intent(this, QuestionActivity.class); startActivity(i); SHOULD I ADD SOMETHING HERE? finish(); ``` Can anyone help me code a solution to my problem? Here is all the code in `QuestionActivity.class` ``` public class QuestionActivity extends SherlockActivity implements OnClickListener { private Question currentQ; private GamePlay currentGame; private Context context; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.question); getSupportActionBar().hide(); /** * Configure current game and get question */ currentGame = ((ChuckApplication) getApplication()).getCurrentGame(); currentQ = currentGame.getNextQuestion(); Button nextBtn = (Button) findViewById(R.id.nextBtn); nextBtn.setOnClickListener(this); Button quitBtn = (Button) findViewById(R.id.quitBtn); quitBtn.setOnClickListener(new OnClickListener() { public void onClick(View arg0) { finish(); } }); //Update the question and answer options.. setQuestions(); final TextView myCounter = (TextView) findViewById(R.id.countdown); new CountDownTimer(20000, 1000) { @Override public void onFinish() { // myCounter.setText("Time up!"); timeUp(context); } @Override public void onTick(long millisUntilFinished) { myCounter.setText("Time left: " + String.valueOf(millisUntilFinished / 1000)); } }.start(); } public void timeUp(Context context) { AlertDialog.Builder builder = new AlertDialog.Builder( QuestionActivity.this); builder.setTitle("Times up!") .setMessage("Game over") .setCancelable(false) .setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { QuestionActivity.this.finish(); } }); AlertDialog alert = builder.create(); alert.show(); } /** * Method to set the text for the question and answers from the current * games current question */ private void setQuestions() { // set the question text from current question String question = Utility.capitalise(currentQ.getQuestion()) + "?"; TextView qText = (TextView) findViewById(R.id.question); qText.setText(question); // set the available options List<String> answers = currentQ.getQuestionOptions(); TextView option1 = (TextView) findViewById(R.id.answer1); option1.setText(Utility.capitalise(answers.get(0))); TextView option2 = (TextView) findViewById(R.id.answer2); option2.setText(Utility.capitalise(answers.get(1))); TextView option3 = (TextView) findViewById(R.id.answer3); option3.setText(Utility.capitalise(answers.get(2))); TextView option4 = (TextView) findViewById(R.id.answer4); option4.setText(Utility.capitalise(answers.get(3))); } public void onClick(View arg0) { //validate a checkbox has been selected if (!checkAnswer()) return; //check if end of game if (currentGame.isGameOver()) { Intent i = new Intent(this, EndgameActivity.class); startActivity(i); finish(); } else { Intent i = new Intent(this, QuestionActivity.class); startActivity(i); finish(); } } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_BACK: return true; } return super.onKeyDown(keyCode, event); } /** * Check if a checkbox has been selected, and if it has then check if its * correct and update gamescore */ private boolean checkAnswer() { String answer = getSelectedAnswer(); if (answer == null) { // Log.d("Questions", "No Checkbox selection made - returning"); return false; } else { // Log.d("Questions", // "Valid Checkbox selection made - check if correct"); if (currentQ.getAnswer().equalsIgnoreCase(answer)) { // Log.d("Questions", "Correct Answer!"); currentGame.incrementRightAnswers(); } else { // Log.d("Questions", "Incorrect Answer!"); currentGame.incrementWrongAnswers(); } return true; } } private String getSelectedAnswer() { RadioButton c1 = (RadioButton) findViewById(R.id.answer1); RadioButton c2 = (RadioButton) findViewById(R.id.answer2); RadioButton c3 = (RadioButton) findViewById(R.id.answer3); RadioButton c4 = (RadioButton) findViewById(R.id.answer4); if (c1.isChecked()) { return c1.getText().toString(); } if (c2.isChecked()) { return c2.getText().toString(); } if (c3.isChecked()) { return c3.getText().toString(); } if (c4.isChecked()) { return c4.getText().toString(); } return null; } ```

Original source