in source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/input/ChoiceSetInputRenderer.java [397:612]
public View renderFilteredComboBox(
RenderedAdaptiveCard renderedCard,
Context context,
ChoiceSetInput choiceSetInput,
HostConfig hostConfig,
RenderArgs renderArgs)
{
final Vector<String> titleList = new Vector<>();
ChoiceInputVector choiceInputVector = choiceSetInput.GetChoices();
long size = choiceInputVector.size();
int valueIndex = -1;
String value = choiceSetInput.GetValue();
for (int i = 0; i < size; i++)
{
ChoiceInput choiceInput = choiceInputVector.get(i);
titleList.addElement(choiceInput.GetTitle());
if (choiceInput.GetValue().equals(value))
{
valueIndex = i;
}
}
boolean usingCustomInputs = isUsingCustomInputs(context);
final AutoCompleteTextView autoCompleteTextView = new ValidatedAutoCompleteTextView(context, usingCustomInputs);
autoCompleteTextView.setThreshold(0);
final AutoCompleteTextViewHandler autoCompleteTextInputHandler = new AutoCompleteTextViewHandler(choiceSetInput);
boolean isRequired = choiceSetInput.GetIsRequired();
ValidatedInputLayout inputLayout = null;
// if using custom inputs, we don't have to create the surrounding linear layout
boolean needsOuterLayout = (!usingCustomInputs);
if (needsOuterLayout)
{
inputLayout = new ValidatedSpinnerLayout(context,
getColor(hostConfig.GetForegroundColor(ContainerStyle.Default, ForegroundColor.Attention, false)));
inputLayout.setTag(new TagContent(choiceSetInput, autoCompleteTextInputHandler));
autoCompleteTextInputHandler.setView(inputLayout);
}
else
{
autoCompleteTextView.setTag(new TagContent(choiceSetInput, autoCompleteTextInputHandler));
autoCompleteTextInputHandler.setView(autoCompleteTextView);
}
renderedCard.registerInputHandler(autoCompleteTextInputHandler, renderArgs.getContainerCardId());
class FilteredChoiceSetAdapter extends ArrayAdapter<String>
{
boolean m_mustWrap = false;
// m_items contains the items currently being displayed as suggestions
// m_originalItemsList contains the items provided by the card author when the element was created
List<String> m_items, m_originalItemsList;
FilteredChoiceSetAdapter(Context context, int resource,
Vector<String> items, boolean mustWrap)
{
super(context, resource, items);
m_mustWrap = mustWrap;
m_items = items;
m_originalItemsList = new ArrayList<>(items);
}
@Override
public int getCount()
{
return m_items.size();
}
@Override
public String getItem(int pos)
{
return m_items.get(pos);
}
@NonNull
@Override
// getView returns the view when spinner is not selected
// override method disables single line setting
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent)
{
View view = super.getView(position, convertView, parent);
TextView txtView = view.findViewById(android.R.id.text1);
if (m_mustWrap)
{
txtView.setSingleLine(false);
}
return view;
}
@NonNull
@Override
public Filter getFilter() {
return m_substringFilter;
}
Filter m_substringFilter = new Filter() {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults filterResults = new FilterResults();
// Due to the time it takes for evaluating all options, this part of the code has
// to be synchronized, otherwise the worker thread that calls the publishResults
// function will throw an illegalstateexception or a concurrentmodificationexception
synchronized (filterResults)
{
List<String> filteredSuggestions = new ArrayList<>();
// isEmpty compares against null and 0-length strings
if (!TextUtils.isEmpty(constraint))
{
String lowerCaseConstraint = constraint.toString().toLowerCase();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
{
Predicate<String> bySubstring = choice -> choice.toLowerCase().contains(lowerCaseConstraint);
filteredSuggestions = m_originalItemsList.stream().filter(bySubstring).collect(Collectors.toList());
}
else
{
for (String choice : m_originalItemsList)
{
if (choice.toLowerCase().contains(lowerCaseConstraint))
{
filteredSuggestions.add(choice);
}
}
}
}
else
{
filteredSuggestions = m_originalItemsList;
}
filterResults.values = filteredSuggestions;
filterResults.count = filteredSuggestions.size();
return filterResults;
}
}
@Override
protected void publishResults(CharSequence constraint, FilterResults filterResults)
{
if (filterResults != null && filterResults.count > 0)
{
m_items = (ArrayList<String>) filterResults.values;
notifyDataSetChanged();
}
else
{
notifyDataSetInvalidated();
}
}
};
}
autoCompleteTextView.setAdapter(new FilteredChoiceSetAdapter(context,
android.R.layout.select_dialog_item,
titleList,
choiceSetInput.GetWrap()));
autoCompleteTextView.setFocusable(true);
if (valueIndex != -1)
{
autoCompleteTextView.setText(titleList.get(valueIndex));
}
autoCompleteTextView.setOnTouchListener(new View.OnTouchListener(){
@Override
public boolean onTouch(View v, MotionEvent event){
autoCompleteTextView.showDropDown();
return false;
}
});
autoCompleteTextView.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View view, int i, KeyEvent keyEvent) {
autoCompleteTextView.showDropDown();
return false;
}
});
autoCompleteTextView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener()
{
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id)
{
CardRendererRegistration.getInstance().notifyInputChange(autoCompleteTextInputHandler.getId(), autoCompleteTextInputHandler.getInput());
}
@Override
public void onNothingSelected(AdapterView<?> parent)
{
CardRendererRegistration.getInstance().notifyInputChange(autoCompleteTextInputHandler.getId(), autoCompleteTextInputHandler.getInput());
}
});
if (needsOuterLayout)
{
inputLayout.addView(autoCompleteTextView);
return inputLayout;
}
else
{
return autoCompleteTextView;
}
}