Home | History | Annotate | Download | only in com.example.android.wearable.quiz
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.example.android.wearable.quiz;
     18 
     19 import static com.example.android.wearable.quiz.Constants.ANSWERS;
     20 import static com.example.android.wearable.quiz.Constants.CHOSEN_ANSWER_CORRECT;
     21 import static com.example.android.wearable.quiz.Constants.CORRECT_ANSWER_INDEX;
     22 import static com.example.android.wearable.quiz.Constants.NUM_CORRECT;
     23 import static com.example.android.wearable.quiz.Constants.NUM_INCORRECT;
     24 import static com.example.android.wearable.quiz.Constants.NUM_SKIPPED;
     25 import static com.example.android.wearable.quiz.Constants.QUESTION;
     26 import static com.example.android.wearable.quiz.Constants.QUESTION_INDEX;
     27 import static com.example.android.wearable.quiz.Constants.QUESTION_WAS_ANSWERED;
     28 import static com.example.android.wearable.quiz.Constants.QUESTION_WAS_DELETED;
     29 import static com.example.android.wearable.quiz.Constants.QUIZ_ENDED_PATH;
     30 import static com.example.android.wearable.quiz.Constants.QUIZ_EXITED_PATH;
     31 import static com.example.android.wearable.quiz.Constants.RESET_QUIZ_PATH;
     32 
     33 import android.app.Activity;
     34 import android.graphics.Color;
     35 import android.net.Uri;
     36 import android.os.Bundle;
     37 import android.util.Log;
     38 import android.view.LayoutInflater;
     39 import android.view.View;
     40 import android.widget.Button;
     41 import android.widget.EditText;
     42 import android.widget.LinearLayout;
     43 import android.widget.RadioGroup;
     44 import android.widget.TextView;
     45 
     46 import com.google.android.gms.common.ConnectionResult;
     47 import com.google.android.gms.common.api.GoogleApiClient;
     48 import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
     49 import com.google.android.gms.common.api.ResultCallback;
     50 import com.google.android.gms.common.data.FreezableUtils;
     51 import com.google.android.gms.wearable.DataApi;
     52 import com.google.android.gms.wearable.DataEvent;
     53 import com.google.android.gms.wearable.DataEventBuffer;
     54 import com.google.android.gms.wearable.DataItem;
     55 import com.google.android.gms.wearable.DataItemBuffer;
     56 import com.google.android.gms.wearable.DataMap;
     57 import com.google.android.gms.wearable.DataMapItem;
     58 import com.google.android.gms.wearable.MessageApi;
     59 import com.google.android.gms.wearable.MessageEvent;
     60 import com.google.android.gms.wearable.Node;
     61 import com.google.android.gms.wearable.NodeApi;
     62 import com.google.android.gms.wearable.PutDataMapRequest;
     63 import com.google.android.gms.wearable.PutDataRequest;
     64 import com.google.android.gms.wearable.Wearable;
     65 
     66 import org.json.JSONArray;
     67 import org.json.JSONException;
     68 import org.json.JSONObject;
     69 
     70 import java.io.IOException;
     71 import java.util.ArrayList;
     72 import java.util.Collections;
     73 import java.util.HashMap;
     74 import java.util.List;
     75 import java.util.Map;
     76 import java.util.PriorityQueue;
     77 
     78 /**
     79  * Allows the user to create questions, which will be put as notifications on the watch's stream.
     80  * The status of questions will be updated on the phone when the user answers them.
     81  */
     82 public class MainActivity extends Activity implements DataApi.DataListener,
     83         MessageApi.MessageListener, ConnectionCallbacks,
     84         GoogleApiClient.OnConnectionFailedListener {
     85 
     86     private static final String TAG = "ExampleQuizApp";
     87     private static final String QUIZ_JSON_FILE = "Quiz.json";
     88 
     89     // Various UI components.
     90     private EditText questionEditText;
     91     private EditText choiceAEditText;
     92     private EditText choiceBEditText;
     93     private EditText choiceCEditText;
     94     private EditText choiceDEditText;
     95     private RadioGroup choicesRadioGroup;
     96     private TextView quizStatus;
     97     private LinearLayout quizButtons;
     98     private LinearLayout questionsContainer;
     99     private Button readQuizFromFileButton;
    100     private Button resetQuizButton;
    101 
    102     private GoogleApiClient mGoogleApiClient;
    103     private PriorityQueue<Question> mFutureQuestions;
    104     private int mQuestionIndex = 0;
    105     private boolean mHasQuestionBeenAsked = false;
    106 
    107     // Data to display in end report.
    108     private int mNumCorrect = 0;
    109     private int mNumIncorrect = 0;
    110     private int mNumSkipped = 0;
    111 
    112     private static final Map<Integer, Integer> radioIdToIndex;
    113 
    114     static {
    115         Map<Integer, Integer> temp = new HashMap<Integer, Integer>(4);
    116         temp.put(R.id.choice_a_radio, 0);
    117         temp.put(R.id.choice_b_radio, 1);
    118         temp.put(R.id.choice_c_radio, 2);
    119         temp.put(R.id.choice_d_radio, 3);
    120         radioIdToIndex = Collections.unmodifiableMap(temp);
    121     }
    122 
    123     @Override
    124     protected void onCreate(Bundle savedInstanceState) {
    125         super.onCreate(savedInstanceState);
    126         setContentView(R.layout.main);
    127 
    128         mGoogleApiClient = new GoogleApiClient.Builder(this)
    129                 .addApi(Wearable.API)
    130                 .addConnectionCallbacks(this)
    131                 .addOnConnectionFailedListener(this)
    132                 .build();
    133         mFutureQuestions = new PriorityQueue<Question>(10);
    134 
    135         // Find UI components to be used later.
    136         questionEditText = (EditText) findViewById(R.id.question_text);
    137         choiceAEditText = (EditText) findViewById(R.id.choice_a_text);
    138         choiceBEditText = (EditText) findViewById(R.id.choice_b_text);
    139         choiceCEditText = (EditText) findViewById(R.id.choice_c_text);
    140         choiceDEditText = (EditText) findViewById(R.id.choice_d_text);
    141         choicesRadioGroup = (RadioGroup) findViewById(R.id.choices_radio_group);
    142         quizStatus = (TextView) findViewById(R.id.quiz_status);
    143         quizButtons = (LinearLayout) findViewById(R.id.quiz_buttons);
    144         questionsContainer = (LinearLayout) findViewById(R.id.questions_container);
    145         readQuizFromFileButton = (Button) findViewById(R.id.read_quiz_from_file_button);
    146         resetQuizButton = (Button) findViewById(R.id.reset_quiz_button);
    147     }
    148 
    149     @Override
    150     protected void onStart() {
    151         super.onStart();
    152         if (!mGoogleApiClient.isConnected()) {
    153             mGoogleApiClient.connect();
    154         }
    155     }
    156 
    157     @Override
    158     protected void onStop() {
    159         Wearable.DataApi.removeListener(mGoogleApiClient, this);
    160         Wearable.MessageApi.removeListener(mGoogleApiClient, this);
    161 
    162         // Tell the wearable to end the quiz (counting unanswered questions as skipped), and then
    163         // disconnect mGoogleApiClient.
    164         DataMap dataMap = new DataMap();
    165         dataMap.putInt(NUM_CORRECT, mNumCorrect);
    166         dataMap.putInt(NUM_INCORRECT, mNumIncorrect);
    167         if (mHasQuestionBeenAsked) {
    168             mNumSkipped += 1;
    169         }
    170         mNumSkipped += mFutureQuestions.size();
    171         dataMap.putInt(NUM_SKIPPED, mNumSkipped);
    172         if (mNumCorrect + mNumIncorrect + mNumSkipped > 0) {
    173             sendMessageToWearable(QUIZ_EXITED_PATH, dataMap.toByteArray());
    174         }
    175 
    176         clearQuizStatus();
    177         super.onStop();
    178     }
    179 
    180     @Override
    181     public void onConnected(Bundle connectionHint) {
    182         Wearable.DataApi.addListener(mGoogleApiClient, this);
    183         Wearable.MessageApi.addListener(mGoogleApiClient, this);
    184     }
    185 
    186     @Override
    187     public void onConnectionSuspended(int cause) {
    188         // Ignore
    189     }
    190 
    191     @Override
    192     public void onConnectionFailed(ConnectionResult result) {
    193         Log.e(TAG, "Failed to connect to Google Play Services");
    194     }
    195 
    196     @Override
    197     public void onMessageReceived(MessageEvent messageEvent) {
    198         if (messageEvent.getPath().equals(RESET_QUIZ_PATH)) {
    199             runOnUiThread(new Runnable() {
    200                 @Override
    201                 public void run() {
    202                     resetQuiz(null);
    203                 }
    204             });
    205         }
    206     }
    207 
    208     /**
    209      * Used to ensure questions with smaller indexes come before questions with larger
    210      * indexes. For example, question0 should come before question1.
    211      */
    212     private static class Question implements Comparable<Question> {
    213 
    214         private String question;
    215         private int questionIndex;
    216         private String[] answers;
    217         private int correctAnswerIndex;
    218 
    219         public Question(String question, int questionIndex, String[] answers,
    220                 int correctAnswerIndex) {
    221             this.question = question;
    222             this.questionIndex = questionIndex;
    223             this.answers = answers;
    224             this.correctAnswerIndex = correctAnswerIndex;
    225         }
    226 
    227         public static Question fromJson(JSONObject questionObject, int questionIndex)
    228                 throws JSONException {
    229             String question = questionObject.getString(JsonUtils.JSON_FIELD_QUESTION);
    230             JSONArray answersJsonArray = questionObject.getJSONArray(JsonUtils.JSON_FIELD_ANSWERS);
    231             String[] answers = new String[JsonUtils.NUM_ANSWER_CHOICES];
    232             for (int j = 0; j < answersJsonArray.length(); j++) {
    233                 answers[j] = answersJsonArray.getString(j);
    234             }
    235             int correctIndex = questionObject.getInt(JsonUtils.JSON_FIELD_CORRECT_INDEX);
    236             return new Question(question, questionIndex, answers, correctIndex);
    237         }
    238 
    239         @Override
    240         public int compareTo(Question that) {
    241             return this.questionIndex - that.questionIndex;
    242         }
    243 
    244         public PutDataRequest toPutDataRequest() {
    245             PutDataMapRequest request = PutDataMapRequest.create("/question/" + questionIndex);
    246             DataMap dataMap = request.getDataMap();
    247             dataMap.putString(QUESTION, question);
    248             dataMap.putInt(QUESTION_INDEX, questionIndex);
    249             dataMap.putStringArray(ANSWERS, answers);
    250             dataMap.putInt(CORRECT_ANSWER_INDEX, correctAnswerIndex);
    251             PutDataRequest putDataRequest = request.asPutDataRequest();
    252             putDataRequest.setUrgent();
    253             return putDataRequest;
    254         }
    255     }
    256 
    257     /**
    258      * Create a quiz, as defined in Quiz.json, when the user clicks on "Read quiz from file."
    259      *
    260      * @throws IOException
    261      */
    262     public void readQuizFromFile(View view) throws IOException, JSONException {
    263         clearQuizStatus();
    264         JSONObject jsonObject = JsonUtils.loadJsonFile(this, QUIZ_JSON_FILE);
    265         JSONArray jsonArray = jsonObject.getJSONArray(JsonUtils.JSON_FIELD_QUESTIONS);
    266         for (int i = 0; i < jsonArray.length(); i++) {
    267             JSONObject questionObject = jsonArray.getJSONObject(i);
    268             Question question = Question.fromJson(questionObject, mQuestionIndex++);
    269             addQuestionDataItem(question);
    270             setNewQuestionStatus(question.question);
    271         }
    272     }
    273 
    274     /**
    275      * Adds a question (with answer choices) when user clicks on "Add Question."
    276      */
    277     public void addQuestion(View view) {
    278         // Retrieve the question and answers supplied by the user.
    279         String question = questionEditText.getText().toString();
    280         String[] answers = new String[4];
    281         answers[0] = choiceAEditText.getText().toString();
    282         answers[1] = choiceBEditText.getText().toString();
    283         answers[2] = choiceCEditText.getText().toString();
    284         answers[3] = choiceDEditText.getText().toString();
    285         int correctAnswerIndex = radioIdToIndex.get(choicesRadioGroup.getCheckedRadioButtonId());
    286 
    287         addQuestionDataItem(new Question(question, mQuestionIndex++, answers, correctAnswerIndex));
    288         setNewQuestionStatus(question);
    289 
    290         // Clear the edit boxes to let the user input a new question.
    291         questionEditText.setText("");
    292         choiceAEditText.setText("");
    293         choiceBEditText.setText("");
    294         choiceCEditText.setText("");
    295         choiceDEditText.setText("");
    296     }
    297 
    298     /**
    299      * Adds the questions (and answers) to the wearable's stream by creating a Data Item
    300      * that will be received on the wearable, which will create corresponding notifications.
    301      */
    302     private void addQuestionDataItem(Question question) {
    303         if (!mHasQuestionBeenAsked) {
    304             // Ask the question now.
    305             Wearable.DataApi.putDataItem(mGoogleApiClient, question.toPutDataRequest());
    306             setHasQuestionBeenAsked(true);
    307         } else {
    308             // Enqueue the question to be asked in the future.
    309             mFutureQuestions.add(question);
    310         }
    311     }
    312 
    313     /**
    314      * Sets the question's status to be the default "unanswered." This will be updated when the
    315      * user chooses an answer for the question on the wearable.
    316      */
    317     private void setNewQuestionStatus(String question) {
    318         quizStatus.setVisibility(View.VISIBLE);
    319         quizButtons.setVisibility(View.VISIBLE);
    320         LayoutInflater inflater = LayoutInflater.from(this);
    321         View questionStatusElem = inflater.inflate(R.layout.question_status_element, null, false);
    322         ((TextView) questionStatusElem.findViewById(R.id.question)).setText(question);
    323         ((TextView) questionStatusElem.findViewById(R.id.status))
    324                 .setText(R.string.question_unanswered);
    325         questionsContainer.addView(questionStatusElem);
    326     }
    327 
    328     @Override
    329     public void onDataChanged(DataEventBuffer dataEvents) {
    330         // Need to freeze the dataEvents so they will exist later on the UI thread
    331         final List<DataEvent> events = FreezableUtils.freezeIterable(dataEvents);
    332         runOnUiThread(new Runnable() {
    333             @Override
    334             public void run() {
    335                 for (DataEvent event : events) {
    336                     if (event.getType() == DataEvent.TYPE_CHANGED) {
    337                         DataMap dataMap = DataMapItem.fromDataItem(event.getDataItem())
    338                                 .getDataMap();
    339                         boolean questionWasAnswered = dataMap.getBoolean(QUESTION_WAS_ANSWERED);
    340                         boolean questionWasDeleted = dataMap.getBoolean(QUESTION_WAS_DELETED);
    341                         if (questionWasAnswered) {
    342                             // Update the answered question's status.
    343                             int questionIndex = dataMap.getInt(QUESTION_INDEX);
    344                             boolean questionCorrect = dataMap.getBoolean(CHOSEN_ANSWER_CORRECT);
    345                             updateQuestionStatus(questionIndex, questionCorrect);
    346                             askNextQuestionIfExists();
    347                         } else if (questionWasDeleted) {
    348                             // Update the deleted question's status by marking it as left blank.
    349                             int questionIndex = dataMap.getInt(QUESTION_INDEX);
    350                             markQuestionLeftBlank(questionIndex);
    351                             askNextQuestionIfExists();
    352                         }
    353                     }
    354                 }
    355             }
    356         });
    357     }
    358 
    359     /**
    360      * Updates the given question based on whether it was answered correctly or not.
    361      * This involves changing the question's text color and changing the status text for it.
    362      */
    363     public void updateQuestionStatus(int questionIndex, boolean questionCorrect) {
    364         LinearLayout questionStatusElement = (LinearLayout)
    365                 questionsContainer.getChildAt(questionIndex);
    366         TextView questionText = (TextView) questionStatusElement.findViewById(R.id.question);
    367         TextView questionStatus = (TextView) questionStatusElement.findViewById(R.id.status);
    368         if (questionCorrect) {
    369             questionText.setTextColor(Color.GREEN);
    370             questionStatus.setText(R.string.question_correct);
    371             mNumCorrect++;
    372         } else {
    373             questionText.setTextColor(Color.RED);
    374             questionStatus.setText(R.string.question_incorrect);
    375             mNumIncorrect++;
    376         }
    377     }
    378 
    379     /**
    380      * Marks a question as "left blank" when its corresponding question notification is deleted.
    381      */
    382     private void markQuestionLeftBlank(int index) {
    383         LinearLayout questionStatusElement = (LinearLayout) questionsContainer.getChildAt(index);
    384         if (questionStatusElement != null) {
    385             TextView questionText = (TextView) questionStatusElement.findViewById(R.id.question);
    386             TextView questionStatus = (TextView) questionStatusElement.findViewById(R.id.status);
    387             if (questionStatus.getText().equals(getString(R.string.question_unanswered))) {
    388                 questionText.setTextColor(Color.YELLOW);
    389                 questionStatus.setText(R.string.question_left_blank);
    390                 mNumSkipped++;
    391             }
    392         }
    393     }
    394 
    395     /**
    396      * Asks the next enqueued question if it exists, otherwise ends the quiz.
    397      */
    398     private void askNextQuestionIfExists() {
    399         if (mFutureQuestions.isEmpty()) {
    400             // Quiz has been completed - send message to wearable to display end report.
    401             DataMap dataMap = new DataMap();
    402             dataMap.putInt(NUM_CORRECT, mNumCorrect);
    403             dataMap.putInt(NUM_INCORRECT, mNumIncorrect);
    404             dataMap.putInt(NUM_SKIPPED, mNumSkipped);
    405             sendMessageToWearable(QUIZ_ENDED_PATH, dataMap.toByteArray());
    406             setHasQuestionBeenAsked(false);
    407         } else {
    408             // Ask next question by putting a DataItem that will be received on the wearable.
    409             Wearable.DataApi.putDataItem(mGoogleApiClient,
    410                     mFutureQuestions.remove().toPutDataRequest());
    411             setHasQuestionBeenAsked(true);
    412         }
    413     }
    414 
    415     private void sendMessageToWearable(final String path, final byte[] data) {
    416         Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).setResultCallback(
    417                 new ResultCallback<NodeApi.GetConnectedNodesResult>() {
    418                     @Override
    419                     public void onResult(NodeApi.GetConnectedNodesResult nodes) {
    420                         for (Node node : nodes.getNodes()) {
    421                             Wearable.MessageApi
    422                                     .sendMessage(mGoogleApiClient, node.getId(), path, data);
    423                         }
    424 
    425                         if (path.equals(QUIZ_EXITED_PATH) && mGoogleApiClient.isConnected()) {
    426                             mGoogleApiClient.disconnect();
    427                         }
    428                     }
    429                 });
    430     }
    431 
    432     /**
    433      * Resets the current quiz when Reset Quiz is pressed.
    434      */
    435     public void resetQuiz(View view) {
    436         // Reset quiz status in phone layout.
    437         for (int i = 0; i < questionsContainer.getChildCount(); i++) {
    438             LinearLayout questionStatusElement = (LinearLayout) questionsContainer.getChildAt(i);
    439             TextView questionText = (TextView) questionStatusElement.findViewById(R.id.question);
    440             TextView questionStatus = (TextView) questionStatusElement.findViewById(R.id.status);
    441             questionText.setTextColor(Color.WHITE);
    442             questionStatus.setText(R.string.question_unanswered);
    443         }
    444         // Reset data items and notifications on wearable.
    445         if (mGoogleApiClient.isConnected()) {
    446             Wearable.DataApi.getDataItems(mGoogleApiClient)
    447                     .setResultCallback(new ResultCallback<DataItemBuffer>() {
    448                         @Override
    449                         public void onResult(DataItemBuffer result) {
    450                             try {
    451                                 if (result.getStatus().isSuccess()) {
    452                                     resetDataItems(result);
    453                                 } else {
    454                                     if (Log.isLoggable(TAG, Log.DEBUG)) {
    455                                         Log.d(TAG, "Reset quiz: failed to get Data Items to reset");
    456                                     }
    457                                 }
    458                             } finally {
    459                                 result.release();
    460                             }
    461                         }
    462                     });
    463         } else {
    464             Log.e(TAG, "Failed to reset data items because client is disconnected from "
    465                     + "Google Play Services");
    466         }
    467         setHasQuestionBeenAsked(false);
    468         mNumCorrect = 0;
    469         mNumIncorrect = 0;
    470         mNumSkipped = 0;
    471     }
    472 
    473     private void resetDataItems(DataItemBuffer dataItemList) {
    474         if (mGoogleApiClient.isConnected()) {
    475             for (final DataItem dataItem : dataItemList) {
    476                 final Uri dataItemUri = dataItem.getUri();
    477                 Wearable.DataApi.getDataItem(mGoogleApiClient, dataItemUri)
    478                         .setResultCallback(new ResetDataItemCallback());
    479             }
    480         } else {
    481             Log.e(TAG, "Failed to reset data items because client is disconnected from "
    482                     + "Google Play Services");
    483         }
    484     }
    485 
    486     /**
    487      * Callback that marks a DataItem, which represents a question, as unanswered and not deleted.
    488      */
    489     private class ResetDataItemCallback implements ResultCallback<DataApi.DataItemResult> {
    490 
    491         @Override
    492         public void onResult(DataApi.DataItemResult dataItemResult) {
    493             if (dataItemResult.getStatus().isSuccess()) {
    494                 PutDataMapRequest request = PutDataMapRequest.createFromDataMapItem(
    495                         DataMapItem.fromDataItem(dataItemResult.getDataItem()));
    496                 DataMap dataMap = request.getDataMap();
    497                 dataMap.putBoolean(QUESTION_WAS_ANSWERED, false);
    498                 dataMap.putBoolean(QUESTION_WAS_DELETED, false);
    499                 if (!mHasQuestionBeenAsked && dataMap.getInt(QUESTION_INDEX) == 0) {
    500                     // Ask the first question now.
    501                     PutDataRequest putDataRequest = request.asPutDataRequest();
    502                     // Set to high priority in case it isn't already.
    503                     putDataRequest.setUrgent();
    504                     Wearable.DataApi.putDataItem(mGoogleApiClient, putDataRequest);
    505                     setHasQuestionBeenAsked(true);
    506                 } else {
    507                     // Enqueue future questions.
    508                     mFutureQuestions.add(new Question(dataMap.getString(QUESTION),
    509                             dataMap.getInt(QUESTION_INDEX), dataMap.getStringArray(ANSWERS),
    510                             dataMap.getInt(CORRECT_ANSWER_INDEX)));
    511                 }
    512             } else {
    513                 Log.e(TAG, "Failed to reset data item " + dataItemResult.getDataItem().getUri());
    514             }
    515         }
    516     }
    517 
    518     /**
    519      * Clears the current quiz when user clicks on "New Quiz."
    520      * On this end, this involves clearing the quiz status layout and deleting all DataItems. The
    521      * wearable will then remove any outstanding question notifications upon receiving this change.
    522      */
    523     public void newQuiz(View view) {
    524         clearQuizStatus();
    525         if (mGoogleApiClient.isConnected()) {
    526             Wearable.DataApi.getDataItems(mGoogleApiClient)
    527                     .setResultCallback(new ResultCallback<DataItemBuffer>() {
    528                         @Override
    529                         public void onResult(DataItemBuffer result) {
    530                             try {
    531                                 if (result.getStatus().isSuccess()) {
    532                                     List<Uri> dataItemUriList = new ArrayList<Uri>();
    533                                     for (final DataItem dataItem : result) {
    534                                         dataItemUriList.add(dataItem.getUri());
    535                                     }
    536                                     deleteDataItems(dataItemUriList);
    537                                 } else {
    538                                     if (Log.isLoggable(TAG, Log.DEBUG)) {
    539                                         Log.d(TAG, "Clear quiz: failed to get Data Items for "
    540                                                 + "deletion");
    541 
    542                                     }
    543                                 }
    544                             } finally {
    545                                 result.release();
    546                             }
    547                         }
    548                     });
    549         } else {
    550             Log.e(TAG, "Failed to delete data items because client is disconnected from "
    551                     + "Google Play Services");
    552         }
    553     }
    554 
    555     /**
    556      * Removes quiz status views (i.e. the views describing the status of each question).
    557      */
    558     private void clearQuizStatus() {
    559         questionsContainer.removeAllViews();
    560         quizStatus.setVisibility(View.INVISIBLE);
    561         quizButtons.setVisibility(View.INVISIBLE);
    562         setHasQuestionBeenAsked(false);
    563         mFutureQuestions.clear();
    564         mQuestionIndex = 0;
    565         mNumCorrect = 0;
    566         mNumIncorrect = 0;
    567         mNumSkipped = 0;
    568     }
    569 
    570     private void deleteDataItems(List<Uri> dataItemUriList) {
    571         if (mGoogleApiClient.isConnected()) {
    572             for (final Uri dataItemUri : dataItemUriList) {
    573                 Wearable.DataApi.deleteDataItems(mGoogleApiClient, dataItemUri)
    574                         .setResultCallback(new ResultCallback<DataApi.DeleteDataItemsResult>() {
    575                             @Override
    576                             public void onResult(DataApi.DeleteDataItemsResult deleteResult) {
    577                                 if (Log.isLoggable(TAG, Log.DEBUG)) {
    578                                     if (deleteResult.getStatus().isSuccess()) {
    579                                         Log.d(TAG, "Successfully deleted data item " + dataItemUri);
    580                                     } else {
    581                                         Log.d(TAG, "Failed to delete data item " + dataItemUri);
    582                                     }
    583                                 }
    584                             }
    585                         });
    586             }
    587         } else {
    588             Log.e(TAG, "Failed to delete data items because client is disconnected from "
    589                     + "Google Play Services");
    590         }
    591     }
    592 
    593     private void setHasQuestionBeenAsked(boolean b) {
    594         mHasQuestionBeenAsked = b;
    595         // Only let user click on Reset or Read from file if they have answered all the questions.
    596         readQuizFromFileButton.setEnabled(!mHasQuestionBeenAsked);
    597         resetQuizButton.setEnabled(!mHasQuestionBeenAsked);
    598     }
    599 }
    600