package org.commcare.views.widgets;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.TypedValue;
import android.view.View;
import android.widget.AdapterView;
import org.commcare.adapters.ComboboxAdapter;
import org.commcare.views.Combobox;
import org.javarosa.core.model.ComboboxFilterRule;
import org.javarosa.core.model.SelectChoice;
import org.javarosa.core.model.data.IAnswerData;
import org.javarosa.core.model.data.InvalidData;
import org.javarosa.core.model.data.SelectOneData;
import org.javarosa.core.model.data.helper.Selection;
import org.javarosa.form.api.FormEntryPrompt;
import java.util.Vector;
/**
* ComboboxWidget is identical to a SpinnerWidget (a select-one widget formatted as a dropdown
* question), but with the added ability to filter the options in the dropdown list by typing into
* an EditText box.
*
* @author Aliza Stone
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public class ComboboxWidget extends QuestionWidget {
private Vector<SelectChoice> choices;
private Vector<String> choiceTexts;
private Combobox comboBox;
public ComboboxWidget(Context context, FormEntryPrompt prompt, ComboboxFilterRule filterRule) {
super(context, prompt);
initChoices(prompt);
comboBox = setUpComboboxForWidget(context, choiceTexts, filterRule, mQuestionFontSize);
addView(comboBox);
comboBox.setEnabled(!prompt.isReadOnly());
comboBox.setFocusable(!prompt.isReadOnly());
addListeners();
fillInPreviousAnswer(prompt);
}
private static Combobox setUpComboboxForWidget(Context context, Vector<String> choices,
ComboboxFilterRule filterRule, int fontSize) {
ComboboxAdapter adapter =
getAdapterForComboboxWidget(context, choices.toArray(new String[]{}),
filterRule, fontSize);
Combobox combobox = new Combobox(context, choices, adapter);
combobox.setTextSize(TypedValue.COMPLEX_UNIT_DIP, fontSize);
return combobox;
}
private static ComboboxAdapter getAdapterForComboboxWidget(Context context, String[] choices,
ComboboxFilterRule filterRule,
int fontSize) {
choices = SpinnerWidget.getChoicesWithEmptyFirstSlot(choices);
ComboboxAdapter adapter = new ComboboxAdapter(context, choices, filterRule);
adapter.setCustomTextSize(fontSize);
return adapter;
}
private void initChoices(FormEntryPrompt prompt) {
choices = prompt.getSelectChoices();
choiceTexts = new Vector<>();
for (int i = 0; i < choices.size(); i++) {
choiceTexts.add(prompt.getSelectChoiceText(choices.get(i)));
}
}
private void addListeners() {
comboBox.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
widgetEntryChanged();
}
});
// Note that Combobox has an OnFocusChangeListener defined in its own class, so when
// re-setting it here we have to make sure to do all of the same things that the original
// implementation does, in addition to the new behavior
comboBox.setOnFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
comboBox.showDropDown();
} else {
comboBox.autoCorrectCapitalization();
widgetEntryChanged();
}
}
});
comboBox.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
clearWarningMessage();
}
});
}
private void fillInPreviousAnswer(FormEntryPrompt prompt) {
if (prompt.getAnswerValue() != null) {
String previousAnswerValue = ((Selection)prompt.getAnswerValue().getValue()).getValue();
for (int i = 0; i < choices.size(); i++) {
if (choices.get(i).getValue().equals(previousAnswerValue)) {
comboBox.setText(choiceTexts.get(i));
break;
}
}
}
}
@Override
public IAnswerData getAnswer() {
// So that we can see any error message that gets shown as a result of this
comboBox.dismissDropDown();
comboBox.autoCorrectCapitalization();
String enteredText = comboBox.getText().toString();
if (choiceTexts.contains(enteredText)) {
int i = choiceTexts.indexOf(enteredText);
return new SelectOneData(new Selection(choices.elementAt(i)));
} else if ("".equals(enteredText)) {
return null;
} else {
return new InvalidData("The text entered is not a valid answer choice",
new SelectOneData(new Selection(enteredText)));
}
}
@Override
public void clearAnswer() {
comboBox.setText("");
}
@Override
public void setFocus(Context context) {
// Intentionally does nothing, in order to force user to click on the combobox themselves
// and thus bring up the drop-down menu (rather than just starting to type without
// realizing there is a dropdown menu available)
}
@Override
public void setOnLongClickListener(OnLongClickListener l) {
}
}