Solution to chapter 6 challegens


#1
  1. The First Challenge:
    Step 1: Create a dimens.xml file to hold the size of the text

     <dimen name="api_level_text_size">14dp</dimen>
    

Step 2 : add a text view in acitivity_cheat.xml file:

   <TextView
    android:id="@+id/api_level_text_view"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:padding="24dp"
    android:textSize="@dimen/api_level_text_size"
    android:typeface="sans"
    tools:text="API level"/>

Step 3 : Add a string resource to hold the text the will be displayed:

<string name="apiLevel">API level %1$s</string>

Step 4: implement code changes in CheatActivity

public class CheatActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_cheat);

    // API level TextView
    TextView apiLevelTextView = (TextView) findViewById(R.id.api_level_text_view);
    apiLevelTextView.setText(getString(R.string.apiLevel, Build.VERSION.RELEASE));

}
}

2.The second Challenge:
Step1: modify both activity_quiz XML layout files (default, and landscape) to add the TextView that will show the number of remaining allowed cheats (TextView as a child or root vertical LinearLayout)

    <TextView
    android:id="@+id/remaining_cheats_text_view"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:padding="8dp"
    tools:text="Allowed Cheats"/>

Step 2 : Add a string resource to hold the text the will be displayed:

<string name="remaining_cheats_count">Allowed Cheats: %1$d</string>

Step 3 : declare a named constant to hold the number of allowed cheats

  private static final int NUMBER_OF_CHEATS_ALLOWED = 3;

Step 4 : declare an instance variable to hold the number of times the user has cheated

private int mCurrentCheatsCount = 0;

Step 5: declare an instance variable to hold the TextView object that will display the number of remaining allowed cheats

private TextView mAllowedCheatsTextView;

Step 6: in OnCreate methd, Wire-up the mAllowedCheatsTextView and remaining_cheats_text_view

mAllowedCheatsTextView = (TextView) findViewById(R.id.remaining_cheats_text_view);

Step 7: make necesary changes so that:

  • The mAllowedCheatsTextView displays the remaining number of allowed cheats all the time, and updates instantly as the cheat happens (i.e: when QuizActivity gets a message that a cheat occurred)
  • when user exceeds number of cheats, Cheat button should deactivated for all remaining question at this cycle (when user finishes all the questions and score is displayed, he can cheat in the new cycle)
  • when the user cheats on a question, there is no need for the cheat button anymore and it should be deactivated for that question in this cycle.
  • The number of times the user has cheated in this cycle should be saved in the bundle to prevent information lose

I will leave you with the code of QuizActivity (I had to copy most of it as this is the best way to understand the changes, Check lines between ** ** ):

public class QuizActivity extends AppCompatActivity {
**private static final String KEY_CHEATS_COUNT = "number of cheats";**
**private static final int NUMBER_OF_CHEATS_ALLOWED = 3;**

**private TextView mAllowedCheatsTextView;**
**private int mCurrentCheatsCount = 0;**

private boolean[] mQuestionStatus = new boolean[mQuestionBank.length];
private boolean[] mCheated = new boolean[mQuestionBank.length];
private int[] mScore = new int[mQuestionBank.length];

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Log.d(TAG, "onCreate() called");
    setContentView(R.layout.activity_quiz);

    if (savedInstanceState != null) {
    .
        .
        .
        if (savedInstanceState.containsKey(KEY_CHEATS_COUNT)) {
            **mCurrentCheatsCount = savedInstanceState.getInt(KEY_CHEATS_COUNT);**
        }
    }
    

    // True button
    mTrueButton = (Button) findViewById(R.id.true_button);
    mTrueButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            checkAnswer(true);
        }
    });

    // False button
    mFalseButton = (Button) findViewById(R.id.false_button);
    mFalseButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            checkAnswer(false);
        }
    });

    // Cheat button
    mCheatButton = (Button) findViewById(R.id.cheat_button) ;
    mCheatButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            boolean answerIsTrue = mQuestionBank[mCurrentIndex].isAnswerTrue();
            Intent intent =
                    CheatActivity.newIntent(QuizActivity.this, answerIsTrue);
            startActivityForResult(intent, REQUEST_CODE_CHEAT);
        }
    });

    // Allowed cheats text view
    **mAllowedCheatsTextView = (TextView) findViewById(R.id.remaining_cheats_text_view);**

    // Next button
    mNextButton = (Button) findViewById(R.id.next_button);
    mNextButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            nextQuestion();
        }
    });

    // Previous button
    mPrevButton = (Button) findViewById(R.id.previous_button);
    mPrevButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            previousQuestion();
        }
    });

    // At app run, display the first question
    updateQuestion();
}

@Override
public void  onSaveInstanceState(Bundle savedInstanceState) {
    super.onSaveInstanceState(savedInstanceState);
    Log.d(TAG, "onSaveInstanceState() called");
    savedInstanceState.putInt(KEY_INDEX, mCurrentIndex);
    savedInstanceState.putBooleanArray(KEY_ANSWERED, mQuestionStatus);
    savedInstanceState.putIntArray(KEY_SCORE, mScore);
    savedInstanceState.putBooleanArray(KEY_CHEATED, mCheated);
    **savedInstanceState.putInt(KEY_CHEATS_COUNT, mCurrentCheatsCount);**
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (resultCode != Activity.RESULT_OK) {
        return;
    }

    if (requestCode == REQUEST_CODE_CHEAT) {
        if (data == null) {
            return;
        }
        mCheated[mCurrentIndex] = CheatActivity.wasAnswerShown(data);
        if (mCheated[mCurrentIndex]) {
            **mCurrentCheatsCount++;**
        }
        // if the user has cheated on the question, disable cheat button
        **mCheatButton.setEnabled(!mCheated[mCurrentIndex]);**
        **displayBalanceCheatsAllowed();**
    }
}

/*
 * Updates the text view with the question statement
 * corresponding to current index
 */
private void updateQuestion() {
    int question = mQuestionBank[mCurrentIndex].getTextResId();
    mQuestionTextView.setText(question);

    // if question has not been answered already, activate answer buttons
    if (!mQuestionStatus[mCurrentIndex]) {
        activateAnswerButton();
    } else {
        deactivateAnswerButton();
    }

    // enable or disable cheat button depending on whether or not user is allowed to cheat
    // and also whether the question has been answered before
    // and user has not cheated before
    **mCheatButton.setEnabled(mCurrentCheatsCount < NUMBER_OF_CHEATS_ALLOWED**
                               **&& !mQuestionStatus[mCurrentIndex]**
                               **&& !mCheated[mCurrentIndex]);**

    **displayBalanceCheatsAllowed();**
}

/*
 * Display a toast message indicating correctness of user answer
 * @param userPressedTrue A falg indicating whether the user answered True
 */
private void checkAnswer(boolean userPressedTrue) {
    // Mark question as answered
    mQuestionStatus[mCurrentIndex] = true;

    deactivateAnswerButton();

    int toastResId;
    if (mCheated[mCurrentIndex]) {
        toastResId = R.string.judgment_toast;
        mScore[mCurrentIndex] = 0;
    } else {
        boolean isCorrectAnswer = userPressedTrue == mQuestionBank[mCurrentIndex].isAnswerTrue();
        if (isCorrectAnswer) {
            mScore[mCurrentIndex] = QUESTION_GRADE;
            toastResId = R.string.correct_toast;
        } else {
            mScore[mCurrentIndex] = 0;
            toastResId = R.string.incorrect_toast;
        }
    }

    Toast toast = Toast.makeText(QuizActivity.this, toastResId,
            Toast.LENGTH_LONG);
    toast.setGravity(Gravity.BOTTOM, 0, 0);
    toast.show();

    // When the user answers all questions(one round, not neccesarrily by order), reset
    if (allAnswered()) {
        DisplayScore();
        reset();
    }
}

/*
 * Checks whether the user has answered all the questions
 * @return true if the user has answered all questions
 */
private boolean allAnswered() {
    for (boolean status : mQuestionStatus) {
        if (!status) {
            return false;
        }
    }
    return true;
}

/*
 * Reset score and answered questions
 */
private void reset() {

    // set all questions unanswered
    for (int i = 0; i < mQuestionStatus.length ; i++) {
        mQuestionStatus[i] = false;
    }

    // reset cheating record
    for (int i = 0; i < mCheated.length ; i++) {
        mCheated[i] = false;
    }

    // set all scores to zero
    for (int i = 0; i < mScore.length ; i++) {
        mScore[i] = 0;
    }

    // reset number of cheats
    **mCurrentCheatsCount = 0;**
}


/*
 * Proceeds to next question
 */
private  void nextQuestion() {
    // keep looping forward in the questions
    mCurrentIndex = (mCurrentIndex + 1) % mQuestionBank.length;
    updateQuestion();
}

/*
 * Returns to previous question
 */
private  void previousQuestion() {
    // keep looping backward in the questions
    if(mCurrentIndex == 0) {
        mCurrentIndex = mQuestionBank.length - 1;
    }  else {
        mCurrentIndex = mCurrentIndex - 1;
    }
    updateQuestion();
}

/*
 * Deactivates True and False buttons
 */
private void deactivateAnswerButton() {
    mTrueButton.setEnabled(false);
    mFalseButton.setEnabled(false);
}

/*
 * Activates True and False buttons
 */
private void activateAnswerButton() {
    mTrueButton.setEnabled(true);
    mFalseButton.setEnabled(true);
}

/*
 * Display the number of remaining allowed cheats
 */
private void displayBalanceCheatsAllowed() {
    **int remainingAllowedCheats = NUMBER_OF_CHEATS_ALLOWED - mCurrentCheatsCount;**
    **mAllowedCheatsTextView.
            setText(getString(R.string.remaining_cheats_count, remainingAllowedCheats));**
}
}