• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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);
temp.put(R.id.choice_a_radio, 0)116         temp.put(R.id.choice_a_radio, 0);
temp.put(R.id.choice_b_radio, 1)117         temp.put(R.id.choice_b_radio, 1);
temp.put(R.id.choice_c_radio, 2)118         temp.put(R.id.choice_c_radio, 2);
temp.put(R.id.choice_d_radio, 3)119         temp.put(R.id.choice_d_radio, 3);
120         radioIdToIndex = Collections.unmodifiableMap(temp);
121     }
122 
123     @Override
onCreate(Bundle savedInstanceState)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
onStart()150     protected void onStart() {
151         super.onStart();
152         if (!mGoogleApiClient.isConnected()) {
153             mGoogleApiClient.connect();
154         }
155     }
156 
157     @Override
onStop()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
onConnected(Bundle connectionHint)181     public void onConnected(Bundle connectionHint) {
182         Wearable.DataApi.addListener(mGoogleApiClient, this);
183         Wearable.MessageApi.addListener(mGoogleApiClient, this);
184     }
185 
186     @Override
onConnectionSuspended(int cause)187     public void onConnectionSuspended(int cause) {
188         // Ignore
189     }
190 
191     @Override
onConnectionFailed(ConnectionResult result)192     public void onConnectionFailed(ConnectionResult result) {
193         Log.e(TAG, "Failed to connect to Google Play Services");
194     }
195 
196     @Override
onMessageReceived(MessageEvent messageEvent)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 
Question(String question, int questionIndex, String[] answers, int correctAnswerIndex)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 
fromJson(JSONObject questionObject, int questionIndex)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
compareTo(Question that)240         public int compareTo(Question that) {
241             return this.questionIndex - that.questionIndex;
242         }
243 
toPutDataRequest()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      */
readQuizFromFile(View view)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      */
addQuestion(View view)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      */
addQuestionDataItem(Question question)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      */
setNewQuestionStatus(String question)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
onDataChanged(DataEventBuffer dataEvents)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      */
updateQuestionStatus(int questionIndex, boolean questionCorrect)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      */
markQuestionLeftBlank(int index)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      */
askNextQuestionIfExists()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 
sendMessageToWearable(final String path, final byte[] data)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      */
resetQuiz(View view)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 
resetDataItems(DataItemBuffer dataItemList)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
onResult(DataApi.DataItemResult dataItemResult)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      */
newQuiz(View view)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      */
clearQuizStatus()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 
deleteDataItems(List<Uri> dataItemUriList)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 
setHasQuestionBeenAsked(boolean b)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