@Override
  public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    if (!isChecked) {
      // If it got unchecked, we don't care.
      return;
    }

    for (RadioButton button : buttons) {
      if (button.isChecked() && !(buttonView == button)) {
        button.setChecked(false);
      }
    }

    SelectChoice choice = mItems.get((Integer) buttonView.getTag());

    if (choice != null) {
      Collect.getInstance()
          .getActivityLogger()
          .logInstanceAction(this, "onCheckedChanged", choice.getValue(), mPrompt.getIndex());
    } else {
      Collect.getInstance()
          .getActivityLogger()
          .logInstanceAction(this, "onCheckedChanged", "<no matching choice>", mPrompt.getIndex());
    }
  }
  protected boolean shouldTreatSingleItemChoiceListAsBooleanField(
      int xFormsDataType, QuestionDef question) {
    if (xFormsDataType != Constants.DATATYPE_CHOICE_LIST) return false;

    if (question.getChoices().size() != 1) return false;

    List<SelectChoice> choices = question.getChoices();
    SelectChoice onlyChoice = choices.get(0);
    if (onlyChoice.getValue().equals(FieldSpec.TRUESTRING)) return true;

    return false;
  }
 public String getMartusAnswerStringFromQuestion(
     IAnswerData answer, QuestionDef question, final int dataType) throws Exception {
   String answerAsString = answer.getDisplayText();
   if (dataType == Constants.DATATYPE_DATE) {
     answerAsString = formatDateToMartusDateFormat(answerAsString);
   } else if (shouldTreatSingleItemChoiceListAsBooleanField(dataType, question)
       && answerAsString.isEmpty()) {
     answerAsString = FieldSpec.FALSESTRING;
   } else if (dataType == Constants.DATATYPE_CHOICE) {
     List<SelectChoice> list = question.getChoices();
     for (int i = 0; i < list.size(); ++i) {
       SelectChoice choice = list.get(i);
       if (choice.getValue().equals(answerAsString)) {
         answerAsString = choice.getLabelInnerText();
         break;
       }
     }
   }
   return answerAsString;
 }
  @Override
  public IAnswerData resolveAnswer(String textVal, TreeElement treeElement, FormDef formDef) {
    QuestionDef questionDef =
        XFormParser.ghettoGetQuestionDef(treeElement.getDataType(), formDef, treeElement.getRef());
    if (questionDef != null
        && (questionDef.getControlType() == Constants.CONTROL_SELECT_ONE
            || questionDef.getControlType() == Constants.CONTROL_SELECT_MULTI)) {
      boolean containsSearchExpression = false;

      XPathFuncExpr xPathExpression = null;
      try {
        xPathExpression =
            ExternalDataUtil.getSearchXPathExpression(questionDef.getAppearanceAttr());
      } catch (Exception e) {
        Log.e(ExternalDataUtil.LOGGER_NAME, e.getMessage(), e);
        // there is a search expression, but has syntax errors
        containsSearchExpression = true;
      }

      if (xPathExpression != null || containsSearchExpression) {
        // that means that we have dynamic selects

        // read the static choices from the options sheet
        List<SelectChoice> staticChoices = questionDef.getChoices();
        for (int index = 0; index < staticChoices.size(); index++) {
          SelectChoice selectChoice = staticChoices.get(index);
          String selectChoiceValue = selectChoice.getValue();
          if (ExternalDataUtil.isAnInteger(selectChoiceValue)) {

            Selection selection = selectChoice.selection();

            switch (questionDef.getControlType()) {
              case Constants.CONTROL_SELECT_ONE:
                {
                  if (selectChoiceValue.equals(textVal)) {
                    // This means that the user selected a static selection.
                    //
                    // Although (for select1 fields) the default implementation will catch this and
                    // return the right thing
                    // (if we call super.resolveAnswer(textVal, treeElement, formDef))
                    // we just need to make sure, so we will override that.
                    if (questionDef.getControlType() == Constants.CONTROL_SELECT_ONE) {
                      // we don't need another, just return the static choice.
                      return new SelectOneData(selection);
                    }
                  }
                }
              case Constants.CONTROL_SELECT_MULTI:
                {
                  // we should search in a potential comma-separated string of values for a match
                  // copied from org.javarosa.xform.util.XFormAnswerDataParser.getSelections()
                  List<String> textValues =
                      DateUtils.split(textVal, XFormAnswerDataSerializer.DELIMITER, true);
                  if (textValues.contains(textVal)) {
                    // this means that the user has selected AT LEAST the static choice.
                    if (selectChoiceValue.equals(textVal)) {
                      // this means that the user selected ONLY the static answer, so just return
                      // that
                      List<Selection> customSelections = new ArrayList<Selection>();
                      customSelections.add(selection);
                      return new SelectMultiData(customSelections);
                    } else {
                      // we will ignore it for now since we will return that selection together with
                      // the dynamic ones.
                    }
                  }
                  break;
                }
              default:
                {
                  // There is a bug if we get here, so let's throw an Exception
                  throw createBugRuntimeException(treeElement, textVal);
                }
            }

          } else {
            switch (questionDef.getControlType()) {
              case Constants.CONTROL_SELECT_ONE:
                {
                  // the default implementation will search for the "textVal" (saved answer) inside
                  // the static choices.
                  // Since we know that there isn't such, we just wrap the textVal in a virtual
                  // choice in order to
                  // create a SelectOneData object to be used as the IAnswer to the TreeElement.
                  // (the caller of this function is searching for such an answer to populate the
                  // in-memory model.)
                  SelectChoice customSelectChoice = new SelectChoice(textVal, textVal, false);
                  customSelectChoice.setIndex(index);
                  return new SelectOneData(customSelectChoice.selection());
                }
              case Constants.CONTROL_SELECT_MULTI:
                {
                  // we should create multiple selections and add them to the pile
                  List<SelectChoice> customSelectChoices = createCustomSelectChoices(textVal);
                  List<Selection> customSelections = new ArrayList<Selection>();
                  for (SelectChoice customSelectChoice : customSelectChoices) {
                    customSelections.add(customSelectChoice.selection());
                  }
                  return new SelectMultiData(customSelections);
                }
              default:
                {
                  // There is a bug if we get here, so let's throw an Exception
                  throw createBugRuntimeException(treeElement, textVal);
                }
            }
          }
        }

        // if we get there then that means that we have a bug
        throw createBugRuntimeException(treeElement, textVal);
      }
    }
    // default behavior matches original behavior (for static selects, etc.)
    return super.resolveAnswer(textVal, treeElement, formDef);
  }