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         private String question;
    214         private int questionIndex;
    215         private String[] answers;
    216         private int correctAnswerIndex;
    217 
    218         public Question(String question, int questionIndex, String[] answers,
    219                 int correctAnswerIndex) {
    220             this.question = question;
    221             this.questionIndex = questionIndex;
    222             this.answers = answers;
    223             this.correctAnswerIndex = correctAnswerIndex;
    224         }
    225 
    226         public static Question fromJson(JSONObject questionObject, int questionIndex)
    227                 throws JSONException {
    228             String question = questionObject.getString(JsonUtils.JSON_FIELD_QUESTION);
    229             JSONArray answersJsonArray = questionObject.getJSONArray(JsonUtils.JSON_FIELD_ANSWERS);
    230             String[] answers = new String[JsonUtils.NUM_ANSWER_CHOICES];
    231             for (int j = 0; j < answersJsonArray.length(); j++) {
    232                 answers[j] = answersJsonArray.getString(j);
    233             }
    234             int correctIndex = questionObject.getInt(JsonUtils.JSON_FIELD_CORRECT_INDEX);
    235             return new Question(question, questionIndex, answers, correctIndex);
    236         }
    237 
    238         @Override
    239         public int compareTo(Question that) {
    240             return this.questionIndex - that.questionIndex;
    241         }
    242 
    243         public PutDataRequest toPutDataRequest() {
    244             PutDataMapRequest request = PutDataMapRequest.create("/question/" + questionIndex);
    245             DataMap dataMap = request.getDataMap();
    246             dataMap.putString(QUESTION, question);
    247             dataMap.putInt(QUESTION_INDEX, questionIndex);
    248             dataMap.putStringArray(ANSWERS, answers);
    249             dataMap.putInt(CORRECT_ANSWER_INDEX, correctAnswerIndex);
    250             return request.asPutDataRequest();
    251         }
    252     }
    253 
    254     /**
    255      * Create a quiz, as defined in Quiz.json, when the user clicks on "Read quiz from file."
    256      * @throws IOException
    257      */
    258     public void readQuizFromFile(View view) throws IOException, JSONException {
    259         clearQuizStatus();
    260         JSONObject jsonObject = JsonUtils.loadJsonFile(this, QUIZ_JSON_FILE);
    261         JSONArray jsonArray = jsonObject.getJSONArray(JsonUtils.JSON_FIELD_QUESTIONS);
    262         for (int i = 0; i < jsonArray.length(); i++) {
    263             JSONObject questionObject = jsonArray.getJSONObject(i);
    264             Question question = Question.fromJson(questionObject, mQuestionIndex++);
    265             addQuestionDataItem(question);
    266             setNewQuestionStatus(question.question);
    267         }
    268     }
    269 
    270     /**
    271      * Adds a question (with answer choices) when user clicks on "Add Question."
    272      */
    273     public void addQuestion(View view) {
    274         // Retrieve the question and answers supplied by the user.
    275         String question = questionEditText.getText().toString();
    276         String[] answers = new String[4];
    277         answers[0] = choiceAEditText.getText().toString();
    278         answers[1] = choiceBEditText.getText().toString();
    279         answers[2] = choiceCEditText.getText().toString();
    280         answers[3] = choiceDEditText.getText().toString();
    281         int correctAnswerIndex = radioIdToIndex.get(choicesRadioGroup.getCheckedRadioButtonId());
    282 
    283         addQuestionDataItem(new Question(question, mQuestionIndex++, answers, correctAnswerIndex));
    284         setNewQuestionStatus(question);
    285 
    286         // Clear the edit boxes to let the user input a new question.
    287         questionEditText.setText("");
    288         choiceAEditText.setText("");
    289         choiceBEditText.setText("");
    290         choiceCEditText.setText("");
    291         choiceDEditText.setText("");
    292     }
    293 
    294     /**
    295      * Adds the questions (and answers) to the wearable's stream by creating a Data Item
    296      * that will be received on the wearable, which will create corresponding notifications.
    297      */
    298     private void addQuestionDataItem(Question question) {
    299         if (!mHasQuestionBeenAsked) {
    300             // Ask the question now.
    301             Wearable.DataApi.putDataItem(mGoogleApiClient, question.toPutDataRequest());
    302             setHasQuestionBeenAsked(true);
    303         } else {
    304             // Enqueue the question to be asked in the future.
    305             mFutureQuestions.add(question);
    306         }
    307     }
    308 
    309     /**
    310      * Sets the question's status to be the default "unanswered." This will be updated when the
    311      * user chooses an answer for the question on the wearable.
    312      */
    313     private void setNewQuestionStatus(String question) {
    314         quizStatus.setVisibility(View.VISIBLE);
    315         quizButtons.setVisibility(View.VISIBLE);
    316         LayoutInflater inflater = LayoutInflater.from(this);
    317         View questionStatusElem = inflater.inflate(R.layout.question_status_element, null, false);
    318         ((TextView) questionStatusElem.findViewById(R.id.question)).setText(question);
    319         ((TextView) questionStatusElem.findViewById(R.id.status))
    320                 .setText(R.string.question_unanswered);
    321         questionsContainer.addView(questionStatusElem);
    322     }
    323 
    324     @Override
    325     public void onDataChanged(DataEventBuffer dataEvents) {
    326         final List<DataEvent> events = FreezableUtils.freezeIterable(dataEvents);
    327         dataEvents.close();
    328         runOnUiThread(new Runnable() {
    329             @Override
    330             public void run() {
    331                 for (DataEvent event : events) {
    332                     if (event.getType() == DataEvent.TYPE_CHANGED) {
    333                         DataMap dataMap = DataMapItem.fromDataItem(event.getDataItem())
    334                                 .getDataMap();
    335                         boolean questionWasAnswered = dataMap.getBoolean(QUESTION_WAS_ANSWERED);
    336                         boolean questionWasDeleted = dataMap.getBoolean(QUESTION_WAS_DELETED);
    337                         if (questionWasAnswered) {
    338                             // Update the answered question's status.
    339                             int questionIndex = dataMap.getInt(QUESTION_INDEX);
    340                             boolean questionCorrect = dataMap.getBoolean(CHOSEN_ANSWER_CORRECT);
    341                             updateQuestionStatus(questionIndex, questionCorrect);
    342                             askNextQuestionIfExists();
    343                         } else if (questionWasDeleted) {
    344                             // Update the deleted question's status by marking it as left blank.
    345                             int questionIndex = dataMap.getInt(QUESTION_INDEX);
    346                             markQuestionLeftBlank(questionIndex);
    347                             askNextQuestionIfExists();
    348                         }
    349                     }
    350                 }
    351             }
    352         });
    353     }
    354 
    355     /**
    356      * Updates the given question based on whether it was answered correctly or not.
    357      * This involves changing the question's text color and changing the status text for it.
    358      */
    359     public void updateQuestionStatus(int questionIndex, boolean questionCorrect) {
    360         LinearLayout questionStatusElement = (LinearLayout)
    361                 questionsContainer.getChildAt(questionIndex);
    362         TextView questionText = (TextView) questionStatusElement.findViewById(R.id.question);
    363         TextView questionStatus = (TextView) questionStatusElement.findViewById(R.id.status);
    364         if (questionCorrect) {
    365             questionText.setTextColor(Color.GREEN);
    366             questionStatus.setText(R.string.question_correct);
    367             mNumCorrect++;
    368         } else {
    369             questionText.setTextColor(Color.RED);
    370             questionStatus.setText(R.string.question_incorrect);
    371             mNumIncorrect++;
    372         }
    373     }
    374 
    375     /**
    376      * Marks a question as "left blank" when its corresponding question notification is deleted.
    377      */
    378     private void markQuestionLeftBlank(int index) {
    379         LinearLayout questionStatusElement = (LinearLayout) questionsContainer.getChildAt(index);
    380         if (questionStatusElement != null) {
    381             TextView questionText = (TextView) questionStatusElement.findViewById(R.id.question);
    382             TextView questionStatus = (TextView) questionStatusElement.findViewById(R.id.status);
    383             if (questionStatus.getText().equals(getString(R.string.question_unanswered))) {
    384                 questionText.setTextColor(Color.YELLOW);
    385                 questionStatus.setText(R.string.question_left_blank);
    386                 mNumSkipped++;
    387             }
    388         }
    389     }
    390 
    391     /**
    392      * Asks the next enqueued question if it exists, otherwise ends the quiz.
    393      */
    394     private void askNextQuestionIfExists() {
    395         if (mFutureQuestions.isEmpty()) {
    396             // Quiz has been completed - send message to wearable to display end report.
    397             DataMap dataMap = new DataMap();
    398             dataMap.putInt(NUM_CORRECT, mNumCorrect);
    399             dataMap.putInt(NUM_INCORRECT, mNumIncorrect);
    400             dataMap.putInt(NUM_SKIPPED, mNumSkipped);
    401             sendMessageToWearable(QUIZ_ENDED_PATH, dataMap.toByteArray());
    402             setHasQuestionBeenAsked(false);
    403         } else {
    404             // Ask next question by putting a DataItem that will be received on the wearable.
    405             Wearable.DataApi.putDataItem(mGoogleApiClient,
    406                     mFutureQuestions.remove().toPutDataRequest());
    407             setHasQuestionBeenAsked(true);
    408         }
    409     }
    410 
    411     private void sendMessageToWearable(final String path, final byte[] data) {
    412         Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).setResultCallback(
    413                 new ResultCallback<NodeApi.GetConnectedNodesResult>() {
    414             @Override
    415             public void onResult(NodeApi.GetConnectedNodesResult nodes) {
    416                 for (Node node : nodes.getNodes()) {
    417                     Wearable.MessageApi.sendMessage(mGoogleApiClient, node.getId(), path, data);
    418                 }
    419 
    420                 if (path.equals(QUIZ_EXITED_PATH) && mGoogleApiClient.isConnected()) {
    421                     mGoogleApiClient.disconnect();
    422                 }
    423             }
    424         });
    425     }
    426 
    427     /**
    428      * Resets the current quiz when Reset Quiz is pressed.
    429      */
    430     public void resetQuiz(View view) {
    431         // Reset quiz status in phone layout.
    432         for(int i = 0; i < questionsContainer.getChildCount(); i++) {
    433             LinearLayout questionStatusElement = (LinearLayout) questionsContainer.getChildAt(i);
    434             TextView questionText = (TextView) questionStatusElement.findViewById(R.id.question);
    435             TextView questionStatus = (TextView) questionStatusElement.findViewById(R.id.status);
    436             questionText.setTextColor(Color.WHITE);
    437             questionStatus.setText(R.string.question_unanswered);
    438         }
    439         // Reset data items and notifications on wearable.
    440         if (mGoogleApiClient.isConnected()) {
    441             Wearable.DataApi.getDataItems(mGoogleApiClient)
    442                     .setResultCallback(new ResultCallback<DataItemBuffer>() {
    443                         @Override
    444                         public void onResult(DataItemBuffer result) {
    445                             if (result.getStatus().isSuccess()) {
    446                                 List<DataItem> dataItemList = FreezableUtils.freezeIterable(result);
    447                                 result.close();
    448                                 resetDataItems(dataItemList);
    449                             } else {
    450                                 if (Log.isLoggable(TAG, Log.DEBUG)) {
    451                                     Log.d(TAG, "Reset quiz: failed to get Data Items to reset");
    452                                 }
    453                             }
    454                             result.close();
    455                         }
    456                     });
    457         } else {
    458             Log.e(TAG, "Failed to reset data items because client is disconnected from "
    459                     + "Google Play Services");
    460         }
    461         setHasQuestionBeenAsked(false);
    462         mNumCorrect = 0;
    463         mNumIncorrect = 0;
    464         mNumSkipped = 0;
    465     }
    466 
    467     private void resetDataItems(List<DataItem> dataItemList) {
    468         if (mGoogleApiClient.isConnected()) {
    469             for (final DataItem dataItem : dataItemList) {
    470                 final Uri dataItemUri = dataItem.getUri();
    471                 Wearable.DataApi.getDataItem(mGoogleApiClient, dataItemUri)
    472                         .setResultCallback(new ResetDataItemCallback());
    473             }
    474         } else {
    475             Log.e(TAG, "Failed to reset data items because client is disconnected from "
    476                     + "Google Play Services");
    477         }
    478     }
    479 
    480     /**
    481      * Callback that marks a DataItem, which represents a question, as unanswered and not deleted.
    482      */
    483     private class ResetDataItemCallback implements ResultCallback<DataApi.DataItemResult> {
    484         @Override
    485         public void onResult(DataApi.DataItemResult dataItemResult) {
    486             if (dataItemResult.getStatus().isSuccess()) {
    487                 PutDataMapRequest request = PutDataMapRequest.createFromDataMapItem(
    488                                 DataMapItem.fromDataItem(dataItemResult.getDataItem()));
    489                 DataMap dataMap = request.getDataMap();
    490                 dataMap.putBoolean(QUESTION_WAS_ANSWERED, false);
    491                 dataMap.putBoolean(QUESTION_WAS_DELETED, false);
    492                 if (!mHasQuestionBeenAsked && dataMap.getInt(QUESTION_INDEX) == 0) {
    493                     // Ask the first question now.
    494                     Wearable.DataApi.putDataItem(mGoogleApiClient, request.asPutDataRequest());
    495                     setHasQuestionBeenAsked(true);
    496                 } else {
    497                     // Enqueue future questions.
    498                     mFutureQuestions.add(new Question(dataMap.getString(QUESTION),
    499                             dataMap.getInt(QUESTION_INDEX), dataMap.getStringArray(ANSWERS),
    500                             dataMap.getInt(CORRECT_ANSWER_INDEX)));
    501                 }
    502             } else {
    503                 Log.e(TAG, "Failed to reset data item " + dataItemResult.getDataItem().getUri());
    504             }
    505         }
    506     }
    507 
    508     /**
    509      * Clears the current quiz when user clicks on "New Quiz."
    510      * On this end, this involves clearing the quiz status layout and deleting all DataItems. The
    511      * wearable will then remove any outstanding question notifications upon receiving this change.
    512      */
    513     public void newQuiz(View view) {
    514         clearQuizStatus();
    515         if (mGoogleApiClient.isConnected()) {
    516             Wearable.DataApi.getDataItems(mGoogleApiClient)
    517                     .setResultCallback(new ResultCallback<DataItemBuffer>() {
    518                         @Override
    519                         public void onResult(DataItemBuffer result) {
    520                             if (result.getStatus().isSuccess()) {
    521                                 List<Uri> dataItemUriList = new ArrayList<Uri>();
    522                                 for (final DataItem dataItem : result) {
    523                                     dataItemUriList.add(dataItem.getUri());
    524                                 }
    525                                 result.close();
    526                                 deleteDataItems(dataItemUriList);
    527                             } else {
    528                                 if (Log.isLoggable(TAG, Log.DEBUG)) {
    529                                     Log.d(TAG, "Clear quiz: failed to get Data Items for deletion");
    530                                 }
    531                             }
    532                             result.close();
    533                         }
    534                     });
    535         } else {
    536             Log.e(TAG, "Failed to delete data items because client is disconnected from "
    537                     + "Google Play Services");
    538         }
    539     }
    540 
    541     /**
    542      * Removes quiz status views (i.e. the views describing the status of each question).
    543      */
    544     private void clearQuizStatus() {
    545         questionsContainer.removeAllViews();
    546         quizStatus.setVisibility(View.INVISIBLE);
    547         quizButtons.setVisibility(View.INVISIBLE);
    548         setHasQuestionBeenAsked(false);
    549         mFutureQuestions.clear();
    550         mQuestionIndex = 0;
    551         mNumCorrect = 0;
    552         mNumIncorrect = 0;
    553         mNumSkipped = 0;
    554     }
    555 
    556     private void deleteDataItems(List<Uri> dataItemUriList) {
    557         if (mGoogleApiClient.isConnected()) {
    558             for (final Uri dataItemUri : dataItemUriList) {
    559                 Wearable.DataApi.deleteDataItems(mGoogleApiClient, dataItemUri)
    560                         .setResultCallback(new ResultCallback<DataApi.DeleteDataItemsResult>() {
    561                             @Override
    562                             public void onResult(DataApi.DeleteDataItemsResult deleteResult) {
    563                                 if (Log.isLoggable(TAG, Log.DEBUG)) {
    564                                     if (deleteResult.getStatus().isSuccess()) {
    565                                         Log.d(TAG, "Successfully deleted data item " + dataItemUri);
    566                                     } else {
    567                                         Log.d(TAG, "Failed to delete data item " + dataItemUri);
    568                                     }
    569                                 }
    570                             }
    571                         });
    572             }
    573         } else {
    574             Log.e(TAG, "Failed to delete data items because client is disconnected from "
    575                     + "Google Play Services");
    576         }
    577     }
    578 
    579     private void setHasQuestionBeenAsked(boolean b) {
    580         mHasQuestionBeenAsked = b;
    581         // Only let user click on Reset or Read from file if they have answered all the questions.
    582         readQuizFromFileButton.setEnabled(!mHasQuestionBeenAsked);
    583         resetQuizButton.setEnabled(!mHasQuestionBeenAsked);
    584     }
    585 }
    586