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